diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce0ea62..c17ac3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,4 +38,4 @@ jobs: POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }} POSTMAN_WORKSPACE_ID: ${{ secrets.POSTMAN_WORKSPACE_ID }} PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - run: fern generate --group external --version ${{ github.ref_name }} --log-level debug + run: fern generate --apis english --group external --version ${{ github.ref_name }} --log-level debug diff --git a/fern/api/definition/api.yml b/fern/api/definition/api.yml deleted file mode 100644 index 6704bf8..0000000 --- a/fern/api/definition/api.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: api -display-name: CodeCombat API -auth: basic -environments: - Production: https://codecombat.com/api -default-environment: Production \ No newline at end of file diff --git a/fern/api/definition/auth.yml b/fern/api/definition/auth.yml deleted file mode 100644 index 78e7345..0000000 --- a/fern/api/definition/auth.yml +++ /dev/null @@ -1,54 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json - -service: - display-name: UserService - auth: true - base-path: /auth/login-o-auth - endpoints: - get: - display-name: Login User - path: "" - method: GET - docs: > - Logs a [user](#users) in. - #### Example - ```javascript - url = - `https://codecombat.com/auth/login-o-auth?provider=${OAUTH_PROVIDER_ID}&accessToken=1234` - res.redirect(url) - // User is sent to this CodeCombat URL and assuming everything checks - out, - // is logged in and redirected to the home page. - ``` - In this example, we call your lookup URL (let's say, - `https://oauth.provider/user?t=<%= accessToken %>`) with the access - token (`1234`). The lookup URL returns `{ id: 'abcd' }` in this case. We - will match this `id` with the OAuthIdentity stored in the user - information in our db. If everything checks out, the user is logged in - and redirected to the home page. - request: - name: GetUserAuthRequest - query-parameters: - provider: - docs: Your OAuth Provider ID - type: string - accessToken: - docs: >- - Will be passed through your lookup URL to get the user ID. - Required if no `code`. - type: optional - code: - docs: >- - Will be passed to the OAuth token endpoint to get a token. - Required if no `accessToken`. - type: optional - redirect: - docs: >- - Override where the user will navigate to after successfully - logging in. - type: optional - errorRedirect: - docs: >- - If an error happens, redirects the user to this url, with at least - query parameters `code`, `errorName` and `message`. - type: optional diff --git a/fern/api/definition/clans.yml b/fern/api/definition/clans.yml deleted file mode 100644 index 92a9d6d..0000000 --- a/fern/api/definition/clans.yml +++ /dev/null @@ -1,41 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json - -imports: - commons: commons.yml - -service: - display-name: Clans Service - auth: true - base-path: /clan/{handle}/members - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - endpoints: - upsertClan: - display-name: Upsert User Into Clan - path: "" - method: PUT - docs: Upserts a user into the clan. - request: - name: UpsertClanRequest - body: - properties: - userId: - docs: The `_id` or `slug` of the user to add to the clan. - type: string - response: ClanResponse - -types: - ClanResponse: - docs: Subset of properties listed here - properties: - _id: optional - name: optional - displayName: optional - members: optional> - ownerID: optional - description: optional - type: optional - kind: optional - metadata: optional> diff --git a/fern/api/definition/classrooms.yml b/fern/api/definition/classrooms.yml deleted file mode 100644 index cf83012..0000000 --- a/fern/api/definition/classrooms.yml +++ /dev/null @@ -1,240 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json - -imports: - commons: commons.yml - -service: - display-name: ClassroomsService - auth: true - base-path: /classrooms - endpoints: - get: - display-name: Get Classroom Details - path: "" - method: GET - docs: Returns the classroom details for a class code. - request: - name: GetClassroomDetailsRequest - query-parameters: - code: - docs: The classroom's `code`. - type: string - retMemberLimit: - docs: limit the return number of members for the classroom - type: optional - response: commons.ClassroomResponseWithCode - - create: - display-name: Create a Classroom - path: "" - method: POST - docs: Creates a new empty `Classroom`. - request: - name: CreateClassroomRequest - body: - properties: - name: - docs: Name of the classroom - type: string - ownerID: commons.objectIdString - aceConfig: AceConfig - - upsertFromClassroom: - display-name: Upsert a User from Classroom - path: /{handle}/members - method: PUT - docs: Upserts a user into the classroom. - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: UpsertClassroomRequest - body: - properties: - code: - docs: The code for joining this classroom - type: string - userId: - docs: The `_id` or `slug` of the user to add to the class. - type: string - retMemberLimit: - docs: >- - limit the return number of members for the classroom, the - default value is 1000 - type: optional - response: commons.ClassroomResponse - - deleteUserFromClassroom: - display-name: Delete User from Classroom - path: /{handle}/members - method: DELETE - docs: Remove a user from the classroom. - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: DeleteUserFromClassroomRequest - body: - properties: - userId: - docs: The `_id` or `slug` of the user to remove from the class. - type: string - retMemberLimit: - docs: >- - limit the return number of members for the classroom, the - default value is 1000 - type: optional - response: commons.ClassroomResponse - - enrollUserInCourse: - display-name: Enroll a User in a Course - path: /{classroomHandle}/courses/{courseHandle}/enrolled - method: PUT - docs: | - Enrolls a user in a course in a classroom. - If the course is paid, user must have an active license. - User must be a member of the classroom. - path-parameters: - classroomHandle: - docs: The classroom's `_id`. - type: string - courseHandle: - docs: The course's `_id`. - type: string - request: - name: EnrollUserInCourseRequest - query-parameters: - retMemberLimit: - docs: >- - limit the return number of members for the classroom, the default - value is 1000 - type: optional - body: - properties: - userId: commons.objectIdString - response: commons.ClassroomResponse - - removeUserFromClassroom: - display-name: Remove a User from a Classroom - path: /{classroomHandle}/courses/{courseHandle}/remove-enrolled - method: PUT - docs: | - Removes an enrolled user from a course in a classroom. - path-parameters: - classroomHandle: - docs: The classroom's `_id`. - type: string - courseHandle: - docs: The course's `_id`. - type: string - request: - name: RemoveUserFromClassroomRequest - query-parameters: - retMemberLimit: - docs: >- - limit the return number of members for the classroom, the default - value is 1000 - type: optional - body: - properties: - userId: commons.objectIdString - response: commons.ClassroomResponse - - getMembersStats: - display-name: Get Members Stats for a Classroom - path: /{classroomHandle}/stats - method: GET - docs: | - Returns a list of all members stats for the classroom. - path-parameters: - classroomHandle: - docs: The classroom's `_id`. - type: string - request: - name: GetMembersStatsRequest - query-parameters: - project: - docs: > - If specified, include only the specified projection of returned - stats; else, return all stats. Format as a comma-separated list, - like `creator,playtime,state.complete`. - type: optional - memberLimit: - docs: >- - Limit the return member number. the default value is 10, and the - max value is 100 - type: optional - memberSkip: - docs: | - Skip the members that doesn't need to return, for pagination - type: optional - response: list - - getLevelSession: - display-name: Get Level Session - path: /{classroomHandle}/members/{memberHandle}/sessions - method: GET - docs: | - Returns a list of all levels played by the user for the classroom. - path-parameters: - classroomHandle: - docs: The classroom's `_id`. - type: string - memberHandle: - docs: The classroom member's `_id`. - type: string - response: list - -types: - LevelSessionResponse: - properties: - state: optional - level: optional - levelID: - docs: Level slug like `wakka-maul` - type: optional - creator: optional - playtime: - docs: Time played in seconds. - type: optional - changed: optional - created: optional - dateFirstCompleted: optional - submitted: - docs: For arenas. Whether or not the level has been added to the ladder. - type: optional - published: - docs: >- - For shareable projects. Whether or not the project has been shared - with classmates. - type: optional - - State: - properties: - complete: optional - - Level: - properties: - original: - docs: The id for the level. - type: optional - - AceConfig: - properties: - language: - docs: Programming language for the classroom - type: optional - - PlayStats: - properties: - gamesCompleted: optional - playtime: - docs: Total play time in seconds - type: optional - - MemberStat: - properties: - _id: optional - stats: optional diff --git a/fern/api/definition/commons.yml b/fern/api/definition/commons.yml deleted file mode 100644 index 7ff061e..0000000 --- a/fern/api/definition/commons.yml +++ /dev/null @@ -1,82 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json - -types: - UserResponse: - docs: Subset of properties listed here - properties: - _id: optional - email: optional - name: optional - slug: optional - role: optional - stats: optional - oAuthIdentities: optional> - subscription: optional - license: optional - - UserStats: - properties: - gamesCompleted: optional - concepts: optional> - playTime: - docs: Included only when specifically requested on the endpoint - type: optional - - AuthIdentity: - properties: - provider: optional - id: optional - - Subscription: - properties: - ends: optional - active: optional - - License: - properties: - ends: optional - active: optional - - objectIdString: string - - roleString: - docs: Usually either 'teacher' or 'student' - type: string - - datetimeString: string - - ClassroomResponseWithCode: - docs: Subset of properties listed here - properties: - _id: optional - name: optional - members: optional> - ownerID: optional - description: optional - code: optional - codeCamel: optional - courses: optional> - clanId: optional - - Course: - properties: - _id: optional - levels: optional>> - enrolled: optional> - instance_id: optional - - ClassroomResponse: - docs: Subset of properties listed here - properties: - _id: optional - name: - type: optional - docs: The name of the classroom - members: - type: optional> - docs: List of _ids of the student members of the classroom - ownerID: - type: optional - docs: The _id of the teacher owner of the classroom. - description: optional - courses: optional> diff --git a/fern/api/definition/stats.yml b/fern/api/definition/stats.yml deleted file mode 100644 index 4092c40..0000000 --- a/fern/api/definition/stats.yml +++ /dev/null @@ -1,57 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json - -service: - display-name: StatsService - auth: true - base-path: / - endpoints: - getPlaytimeStats: - display-name: Get Playtime Stats - path: /playtime-stats - method: GET - docs: Returns the playtime stats - request: - name: GetPlaytimeStats - query-parameters: - startDate: - docs: Earliest an included user was created - type: optional - endDate: - docs: Latest an included user was created - type: optional - country: - docs: Filter by country string - type: optional - response: PlaytimeStatsResponse - - getLicenseStats: - display-name: Get License Stats - path: /license-stats - method: GET - docs: Returns the license stats - response: LicenseStatsResponse - -types: - PlaytimeStatsResponse: - properties: - playTime: - docs: Total play time in seconds - type: optional - gamesPlayed: - docs: Number of levels played - type: optional - - LicenseStatsResponse: - properties: - licenseDaysGranted: - docs: Total number of license days granted - type: optional - licenseDaysUsed: - docs: Number of license days used - type: optional - licenseDaysRemaining: - docs: Number of license days remaining - type: optional - activeLicenses: - docs: Number of active/valid licenses - type: optional diff --git a/fern/api/definition/users.yml b/fern/api/definition/users.yml deleted file mode 100644 index 7dc79f1..0000000 --- a/fern/api/definition/users.yml +++ /dev/null @@ -1,326 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json - -imports: - commons: commons.yml - -service: - display-name: UsersService - auth: true - base-path: / - endpoints: - create: - display-name: Create User - path: /users - method: POST - docs: | - Creates a `User`. - #### Example - ```javascript - url = 'https://codecombat.com/api/users' - json = { email: 'an@email.com', name: 'Some Username', role: 'student' } - request.post({ url, json, auth }) - ``` - request: - name: CreateUserRequest - body: - properties: - name: string - email: string - role: - docs: >- - A `"student"` or `"teacher"`. If unset, a home user will be - created, unable to join classrooms. - type: optional - preferredLanguage: optional - heroConfig: optional - birthday: optional - - get: - display-name: Get User - path: /users/{handle} - method: GET - docs: Returns a `User`. - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: GetUserRequest - query-parameters: - includePlayTime: - docs: Set to non-empty string to include stats.playTime in response - type: optional - response: commons.UserResponse - - update: - display-name: Update User - path: /users/{handle} - method: PUT - docs: Modify name of a `User` - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: UpdateUserRequest - body: - properties: - name: - docs: Set to new name string - type: string - birthday: - docs: Set the birthday - type: optional - response: commons.UserResponse - - getClassrooms: - display-name: Get Classrooms By User - path: /users/{handle}/classrooms - method: GET - docs: >- - Returns a list of `Classrooms` this user is in (if a student) or owns - (if a teacher). - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: GetClassroomsRequest - query-parameters: - retMemberLimit: - docs: limit the return number of members for each classroom - type: optional - response: list - - getHero: - display-name: Get User Hero - path: /users/{handle}/hero-config - method: PUT - docs: Set the user's hero. - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: GetHeroRequest - body: - properties: - thangType: optional - response: commons.UserResponse - - setAceConfig: - path: /users/{handle}/ace-config - method: PUT - docs: >- - Set the user's aceConfig (the settings for the in-game Ace code editor), - such as whether to enable autocomplete. - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: SetAceConfig - body: - properties: - liveCompletion: - docs: >- - controls whether autocompletion snippets show up, the default - value is true - type: optional - behaviors: - docs: >- - controls whether things like automatic parenthesis and quote - completion happens, the default value is false - type: optional - language: - docs: >- - only for home users, should be one of ["python", "javascript", - "cpp", "lua", "coffeescript"] right now - type: optional - response: commons.UserResponse - - addOAuthIdentity: - display-name: Add OAuth2 Identity - path: /users/{handle}/o-auth-identities - method: POST - docs: > - Adds an OAuth2 identity to the user, so that they can be logged in with - that identity. You need to send the OAuth code or the access token to - this endpoint. - 1. If no access token is provided, it will use your OAuth2 token URL to - exchange the given code for an access token. - 1. Then it will use the access token (given by you, or received from - step 1) to look up the user on your service using the lookup URL, and - expects a JSON object in response with an `id` property. - 1. It will then save that user `id` to the user in our db as a new - OAuthIdentity. - #### Example - ```javascript - url = `https://codecombat.com/api/users/${userID}/o-auth-identities` - OAUTH_PROVIDER_ID = 'xyz' - json = { provider: OAUTH_PROVIDER_ID, accessToken: '1234' } - request.post({ url, json, auth}, (err, res) => { - console.log(res.body.oAuthIdentities) // [ { provider: 'xyx', id: 'abcd' } ] - }) - ``` - In this example, we call your lookup URL (let's say, - `https://oauth.provider/user?t=<%= accessToken %>`) with the access - token (`1234`). The lookup URL returns `{ id: 'abcd' }` in this case, - which we save to the user in our db. - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: AddOAuthIdentityRequest - body: - properties: - provider: - docs: Your OAuth Provider ID. - type: string - accessToken: - docs: >- - Will be passed through your lookup URL to get the user ID. - Required if no `code`. - type: optional - code: - docs: >- - Will be passed to the OAuth token endpoint to get a token. - Required if no `accessToken`. - type: optional - response: commons.UserResponse - - updateSubscription: - path: /users/{handle}/subscription - method: PUT - docs: | - Grants a user premium access to the "Home" version up to a certain time. - #### Example - ```javascript - url = `https://codecombat.com/api/users/${userID}/subscription` - json = { ends: new Date('2017-01-01').toISOString() } - request.put({ url, json, auth }, (err, res) => { - console.log(res.body.subscription) // { ends: '2017-01-01T00:00:00.000Z', active: true } - }) - ``` - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: UpdateSubscriptionRequest - body: - properties: - ends: datetime - response: commons.UserResponse - - shortenSubscription: - display-name: Shorten User Subscription - path: /users/{handle}/shorten-subscription - method: PUT - docs: > - If the user already has a premium access up to a certain time, this - shortens/revokes his/her premium access. - If the ends is less than or equal to the current time, it revokes the - subscription and sets the end date to be the current time, else it just - shortens the subscription. - #### Example - ```javascript - url = `https://codecombat.com/api/users/${userID}/shorten-subscription` - json = { ends: new Date().toISOString() } - request.put({ url, json, auth }, (err, res) => { - console.log(res.body.subscription.active) // false - }) - ``` - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: ShortenSubscriptionRequest - body: - properties: - ends: datetime - response: commons.UserResponse - - grantLicense: - display-name: Grant User License - path: /users/{handle}/license - method: PUT - docs: | - Grants a user access to the "Classroom" version up to a certain time. - Sets their role to "student". - #### Example - ```javascript - url = `https://codecombat.com/api/users/${userID}/license` - json = { ends: new Date('2017-01-01').toISOString() } - request.put({ url, json, auth }, (err, res) => { - console.log(res.body.license) // { ends: '2017-01-01T00:00:00.000Z', active: true } - }) - ``` - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: GrantLicenseRequest - body: - properties: - ends: datetime - response: commons.UserResponse - - shortenLicense: - display-name: Shorten User License - path: /users/{handle}/shorten-license - method: PUT - docs: > - If the user already has access to the "Classroom" version up to a - certain time, this shortens/revokes his/her access. - If the ends is less than or equal to the current time, it revokes the - enrollment and sets the end date to be the current time, else it just - shortens the enrollment. - #### Example - ```javascript - url = `https://codecombat.com/api/users/${userID}/shorten-license` - json = { ends: new Date().toISOString() } - request.put({ url, json, auth }, (err, res) => { - console.log(res.body.license.active) // false - }) - ``` - path-parameters: - handle: - docs: The document's `_id` or `slug`. - type: string - request: - name: ShortenLicenseRequest - body: - properties: - ends: datetime - response: commons.UserResponse - - #TODO feels like a chain of endpoint to endpoint actions - findUser: - display-name: Search for User - path: /user-lookup/{property}/{value} - method: GET - docs: Redirects to `/users/{handle}` given a unique, identifying property - path-parameters: - property: - docs: The property to lookup by. May either be `"israel-id"` or `"name"`. - type: string - value: - docs: The value to be looked up. - type: string - -types: - UserRole: - docs: >- - A `"student"` or `"teacher"`. If unset, a home user will be created, - unable to join classrooms. - enum: - - student - - teacher - - HeroConfig: - properties: - thangType: optional diff --git a/fern/apis/chinese/generators.yml b/fern/apis/chinese/generators.yml new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/fern/apis/chinese/generators.yml @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/fern/apis/chinese/openapi/openapi.yml b/fern/apis/chinese/openapi/openapi.yml new file mode 100644 index 0000000..4fa7965 --- /dev/null +++ b/fern/apis/chinese/openapi/openapi.yml @@ -0,0 +1,1025 @@ +swagger: "2.0" +info: + version: "0.0.0" + title: CodeCombat开发者接口 + description: | + ## 基本情况 + + * 开发者在Node/Express服务器上安装了以下 [请求](https://github.com/request/request),示例采用JavaScript形式。 + * 请求和反馈形式为JSON。 + * API接口是创建或者参考的基础资源。因此,举例来说,所有的开发路径(routes)是以'/api/users'开始,返回[User](#users)开发资源。 + + ## 客户设置 + 目前我们没有让您创建或者设置您自己的客户级开发者接口(API Client)或者OAuth提供者(OAuth Privider)信息。请直接联系我们启动您的开发进程。 + + ## 用户授权 + 系统必须通过基础HTTP授权(Basic HTTP Authentication)来启动API路径。在我们的系统中创建你的API客户(API_CLIENT)之后,你会获得一个用户名(CLIENT_ID)和密码(CLIENT_SECRET)。对于每一个API请求,请提供你的姓名(CLIENT_ID)和密码(CLIENT_SECRET)。 + + ```javascript + url = 'https://koudashijie.com/api/users' + json = { name: 'A username' } + auth = { name: CLIENT_ID, pass: CLIENT_SECRET } + request.get({ url, json, auth }, (err, res) => console.log(res.statusCode, res.body)) + ``` + + ## 用户授权 + 通过您的服务在CodeCombat平台上认证一个用户,您需要采用以下oAuth 2流程. CodeCombat作为一个客户,您的服务作为服务提供者。首先,您需要提供一个可信的查找网址(lookup URL 或者 token URL),来进行创建设置(参见以上客户创建说明),这个创建账户和登陆的过程如以下所示: + + 1. **创建用户** 使用[POST/api/users](#users/post_users). + 1. **把CodeCombat用户和一个oAuth身份对接** 使用[POST/api/users/:handle/o-auth-identities](#users/post_users__handle__o_auth_identities). + 你可以使用代码或者准入指令(access token)来启动这个API嵌入。如果你没有获得准入指令(access token),我们会用这个URL指令来替换原有的代码来获得一个准入指令(access token)。 + 然后我们用准入指令(access token)来启动查找网址(lookup URL),以此从你的系统中获得用户信息(`id`),这些信息已被储存在我们的用户数据库中。 + 1. **用户登陆** 重新引导用户到以下路径 [/auth/login-o-auth](#auth/get_auth_login_o_auth). + 你可以用这个代码/准入指令(access token)来call API, 然后我们会通过类似于第二步中的步骤来获得用户信息。 + 最后,我们将数据库在第二步中获得的信息与这个信息进行匹配,如果匹配成功,用户即可完成登录并被引导到主页。 + + 这里还有一个描述以上过程的 [实例](https://s3.amazonaws.com/files.codecombat.com/codecombat_oauth_example.tar.gz),以便理解。 同时,你也可以参考这个 [图表](https://s3.amazonaws.com/files.codecombat.com/Example_OAuth_Flow.png)。 + +host: koudashijie.com +basePath: /api +schemes: + - https +consumes: + - application/json +produces: + - application/json + +paths: + /auth/login-o-auth: + get: + summary: 登录用户 + tags: + - auth + operationId: loginOauth + description: | + 用 [user](#users) 登陆. + + 在这个示例中,我们用准入指令(access token)(`1234`)来call你的查找的URL(lookup URL)(比方说,是 `https://oauth.provider/user?t=<%=accessToken%>` 。在这个例子中,返回的查找URL(lookup URL)是 `{ id: 'abcd' }`。我们将数据库中储存的OAuthIdentity用户信息与这个`id`匹配。如果匹配成功,用户即可登录并被引导到主页。 + + parameters : + - name: provider + in: query + required: true + type: string + description: 你的授权(oAuth)提供者ID + - name: accessToken + in: query + type: string + description: 通过传输你的查询网址以便于拿到用户ID。如果没有“代码”这一环节是必须项。 + - name: code + type: string + in: query + description: 通过传输到授权指令的终端以便于拿到指令。如果没有“代码”这一环节是必须项。如果没有`accessToken`(“访问指令”)这一环节是必须项。 + - name: redirect + type: string + in: query + description: 登陆成功后,用户导航路径会被清除。 + - name: errorRedirect + type: string + in: query + description: 如果错误出现,将用户重置于这个网址,请求参数至少需要`code`(“代码”)、`errorName`(“错误名称”)和`message`(“消息”)等字段。 + responses: + "302": + description: '用户成功登录以后,将用户重置于登陆页面。' + + /users: + post: + summary: 创建用户 + tags: + - users + operationId: users-create + description: | + 创建一个`User`(“用户”) + + parameters: + - name: user + in: body + schema: + type: object + required: + - name + - email + properties: + name: + type: string + email: + type: string + role: + type: string + description: | + `"student"` 或者 `"teacher"` + enum: + - student + - teacher + preferredLanguage: + type: string + heroConfig: + type: object + properties: + thangType: + $ref: '#/definitions/objectIdString' + birthday: + type: string + responses: + "201": + description: '已经创建的用户' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}: + get: + summary: 获取用户 + tags: + - users + operationId: users-get + description: 返回一个`User`(“用户”)。 + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: includePlayTime + in: query + description: "设置非空字符串包括反馈函数stats.playTime" + required: false + type: string + responses: + "200": + description: '需求用户' + schema: + $ref: '#/definitions/UserResponse' + + put: + tags: + - users + operationId: users-update + description: 修改一个 `User`("用户") 的姓名 + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: user + in: body + required: true + schema: + type: object + properties: + name: + type: string + description: '设置一个新姓名' + birthday: + type: string + description: '设置生日' + responses: + "200": + description: '影响的用户' + schema: + $ref: '#/definitions/UserResponse' + + + + /users/{handle}/classrooms: + get: + summary: 按用户获取教室 + tags: + - users + operationId: users-get-classrooms + description: 返回`Clasrooms`(“教室”)清单,这个用户(如果是学生)在教室里或者是教室的主人(如果是老师)。 + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: retMemberLimit + in: query + description: "设置返回的教室中显示的学生数量" + required: false + type: number + responses: + "200": + description: '教室需求' + schema: + type: array + items: + $ref: '#/definitions/ClassroomResponseWithCode' + + /users/{handle}/hero-config: + put: + summary: 获取用户英雄 + tags: + - users + operationId: users-set-hero + description: 设置用户的英雄。 + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: heroConfig + in: body + schema: + type: object + properties: + thangType: + $ref: '#/definitions/objectIdString' + responses: + "200": + description: '受影响的客户' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/ace-config: + put: + summary: 设置用户的代码配置 + tags: + - users + operationId: users-set-ace-config + description: 设置用户的代码配置 ( 指用户在编辑器中可以修改的设置 ) , 如代码补全等. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: aceConfig + in: body + schema: + type: object + properties: + liveCompletion: + type: boolean + description: '控制代码自动补全提示是否出现, 默认值是 true' + behaviors: + type: boolean + description: '控制括号, 引号是否自动补齐, 默认值是 false' + language: + type: string + description: '控制个人账号(学生账号无效)使用的语言, 目前仅支持 python, javascript, cpp, lua, coffeescript' + responses: + "200": + description: '受影响的客户' + schema: + $ref: '#/definitions/UserResponse' + + + /users/{handle}/o-auth-identities: + post: + summary: 将一个OAuth2身份 + tags: + - users + operationId: users-link-oauth-identity + description: | + 将一个OAuth2身份(OAuth2 identity)加给用户,用户可以以此身份登录。你需要将一个OAuth代码或者准入指令(access token)发送到这个端口。 + + 1. 如果没有获得准入指令(access token)的话,系统会使用OAuth2指令URL(OAuth2 token URL)来交换获得的代码,以此获得准入指令(access token)。 + 2. 然后系统会在你的服务上使用查找URL(lookup URL)爱你的通过准入指令(access token) (改指令可能是你之前给予的,或者是在第一步中获得的)来查找这名用户。 并预计获得一个回应一个`id`属性的JSON对象。 + 3. 之后系统将保存用户`id`到我们的用户数据库中,作为一个新的OAuthIdentity. + + 在这个示例中, 我们用准入指令(access token)(`1234`)来call你的查找网址(lookup URL)(比方说是 `https://oauth.provider/user?t=<%= accessToken %>`)然后查找网址(lookup URL)返回`{ id: 'abcd' }`,我们已经将这个用户储存到我们的数据库中了。 + + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: oAuthIdentity + in: body + required: true + schema: + type: object + required: + - provider + properties: + provider: + type: string + description: 你的授权提供者 ID (OAuth Provider ID)。 + accessToken: + type: string + description: 将传输查询网址得到用户ID。如果没有`code`(“代码”)需要这一项。 + code: + type: string + description: 将传输授权指令终点得到令牌。如果没有`accessToken`(“访问令牌”)需要这一项。 + + responses: + "200": + description: '受影响用户' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/subscription: + put: + summary: 授予订阅 + tags: + - users + operationId: users-grant-premium-subscription + description: | + 在一段时间内授权用户访问“主页”版本的高级访问权限。 + + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: subscription + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: '受影响用户' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/shorten-subscription: + put: + summary: 撤销他的高级访问权限 + tags: + - users + operationId: users-shorten-subscription + description: | + 如果用户在一段时间已经有了高级访问权限,这将缩短/撤销他的高级访问权限。 + 如果结束时间少于或者等于现在时间,将撤销订阅权限并将结束时间设置为现在时间,否则将缩短订阅时间。 + + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: subscription + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: '受影响客户' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/license: + put: + summary: 格兰特“课堂”版 + tags: + - users + operationId: users-grant-license + description: | + 在一段时间内给予用户使用“教室”版本的权限。定义他们的角色为`"student"`(“学生”)。 + + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: license + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: '受影响用户' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/shorten-license: + put: + summary: 缩短用户许可 + tags: + - users + operationId: users-shorten-license + description: | + 如果用户在一定时间内已经登陆“教室”版本,这将缩短或撤销他的权限。 + 如果结束比现在时间短或者和现在时间相同, 这将撤销登陆,并将结束时间设置为当前时间,否则这将缩短注册时间。 + + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: license + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: '受影响客户' + schema: + $ref: '#/definitions/UserResponse' + + /clan/{handle}/members: + put: + summary: 将用户添加到部落 + tags: + - clans + operationId: clans-upsert-member + description: 在部落中加入一个用户 + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: member + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + description: "使用字符串 `_id` 或 `slug` 来识别要加入部落的用户." + type: string + responses: + "200": + description: '返回加入了新成员的部落' + schema: + $ref: '#/definitions/ClanResponse' + + + /classrooms: + post: + summary: 创建一个教室 + tags: + - classrooms + operationId: classrooms-create + description: 创建一个新的空“教室” + parameters: + - name: classroom + in: body + required: true + schema: + type: object + required: + - name + - ownerID + - aceConfig + properties: + name: + type: string + description: 教室名称 + ownerID: + $ref: '#/definitions/objectIdString' + description: 教师 `_id` 是指哪间教室被创建了 + aceConfig: + type: object + properties: + language: + type: string + description: 适合教室的编程语言 + responses: + "201": + description: '创建的教室' + schema: + $ref: '#/definitions/ClassroomResponseWithCode' + + get: + summary: 获得一间教室 + tags: + - classrooms + operationId: classrooms-get + description: 教室模式之返回教室细节. + parameters: + - name: code + in: query + type: string + required: true + description: 教室 `code`(”代码”) + - name: retMemberLimit + in: query + description: "设置返回的教室中显示的学生数量" + required: false + type: number + responses: + "200": + description: '教室细节' + schema: + $ref: '#/definitions/ClassroomResponseWithCode' + + /classrooms/{handle}/members: + put: + summary: 添加会员 + tags: + - classrooms + operationId: classrooms-upsert-member + description: 在教室插入一个用户. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: member + in: body + required: true + schema: + type: object + required: + - code + - userId + properties: + code: + type: string + description: "加入教室需使用代码" + userId: + description: "使用字符串 `_id` 或`slug` 用来在教室中增加用户." + type: string + retMemberLimit: + description: "设置返回的教室中显示的学生数量, 默认值为 1000" + type: number + responses: + "200": + description: '教室中新增新成员' + schema: + $ref: '#/definitions/ClassroomResponse' + + delete: + tags: + - classrooms + summary: 从教室中移出一个学生 + operationId: classrooms-remove-member + description: 从教室中移出一个学生 + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: member + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + description: "使用字符串 `_id` 或`slug` 用来从教室中移出用户." + type: string + retMemberLimit: + description: "设置返回的教室中显示的学生数量, 默认值为 1000" + type: number + responses: + "200": + description: '移出了学生的教室' + schema: + $ref: '#/definitions/ClassroomResponse' + + + /classrooms/{classroomHandle}/courses/{courseHandle}/enrolled: + put: + summary: 在课程中注册用户 + tags: + - classrooms + operationId: classrooms-enroll-user-in-course + description: | + 在一个教室里的一门课程上注册一个用户。 + 如果课程是付费课程,用户需要一个有效的订阅许可。 + 用户必须是教室中的一个成员。 + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: 教室 `_id`. + - name: courseHandle + in: path + type: string + required: true + description: 课程 `_id`. + - name: userId + description: "用户的`_id`或者`slug`需要添加到教室中。" + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + $ref: '#/definitions/objectIdString' + - name: retMemberLimit + in: query + description: "设置返回的教室中显示的学生数量, 默认值为 1000" + required: false + type: number + + responses: + "200": + description: '教室并且用户加入其中。' + schema: + $ref: '#/definitions/ClassroomResponse' + + /classrooms/{classroomHandle}/courses/{courseHandle}/remove-enrolled: + put: + tags: + - classrooms + summary: 删除注册用户 + operationId: classrooms-remove-enrolled-user + description: | + 将用户从注册的课程和教室中移除。 + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: 教室`_id`. + - name: courseHandle + in: path + type: string + required: true + description: 课程`_id`. + - name: userId + description: "用户的`_id`或者`slug`。" + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + $ref: '#/definitions/objectIdString' + - name: retMemberLimit + in: query + description: "设置返回的教室中显示的学生数量, 默认值为 1000" + required: false + type: number + responses: + "200": + description: '教室中,将用户移除课程。' + schema: + $ref: '#/definitions/ClassroomResponse' + + /classrooms/{classroomHandle}/stats: + get: + summary: 获取会员统计信息 + tags: + - classrooms + operationId: classrooms-get-members-stats + description: | + 返回教室中所有学生的相关数据 + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: 教室 `_id`. + - name: project + in: query + type: string + description: | + 限制返回的数据内容, 由以下字符串按逗号连接组成: ["creator", "playtime", "state.complete"...] + required: false + - name: memberLimit + in: query + type: number + description: '返回值中的学生数量, 默认为 10, 最大值为 100' + required: false + - name: memberSkip + in: query + type: number + description: '返回值中跳过的学生数量, 和 memberLimit 共同起到分页作用' + required: false + + responses: + "200": + description: | + 返回教室中所有学生的相关数据 + schema: + type: array + items: + type: object + properties: + _id: + $ref: '#/definitions/objectIdString' + stats: + type: object + properties: + gamesCompleted: + type: number + playtime: + type: number + description: "游戏时间(秒)" + + + /classrooms/{classroomHandle}/members/{memberHandle}/sessions: + get: + summary: 玩过关卡 + tags: + - classrooms + operationId: classrooms-get-levels-played + description: | + 返回这个教室中用户所玩得所有关卡. + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: 教室 `_id`. + - name: memberHandle + in: path + type: string + required: true + description: 教室成员 `_id`. + responses: + "200": + description: '教室的用户已经注册.' + schema: + type: array + items: + $ref: '#/definitions/LevelSessionResponse' + + /user-lookup/{property}/{value}: + get: + summary: 搜索用户 + tags: + - users + operationId: users-lookup + description: 采用唯一、可识别属性重新定向到`/users/{handle}` + parameters: + - name: property + in: path + type: string + required: true + description: 查找属性对应字符串,也许是 `"israel-id"` 或者 `"name"`. + - name: value + in: path + type: string + required: true + description: 查找的值. + responses: + "301": + description: "重新定位用户的资源到`/users/{handle}`" + + /playtime-stats: + get: + summary: 获取游戏时间统计数据 + tags: + - stats + operationId: stats-get-playtime-stats + description: 返回游戏时间统计 + parameters: + - name: startDate + in: query + description: "早先有一个已经注册的用户重新创建" + required: false + type: string + - name: endDate + in: query + description: "早先有一个已经注册的用户重新创建" + required: false + type: string + - name: country + in: query + description: "按照国家字符串进行过滤" + required: false + type: string + + responses: + "200": + description: "反馈所有自有用户的游戏时间统计." + schema: + $ref: '#/definitions/PlaytimeStatsResponse' + + /license-stats: + get: + summary: 获取许可证统计信息 + tags: + - stats + operationId: stats-get-license-stats + description: 返回订阅数据统计 + + responses: + "200": + description: "对于教室/家庭订阅许可返回订阅统计数据。" + schema: + $ref: '#/definitions/LicenseStatsResponse' + +parameters: + handlePathParameter: + name: handle + in: path + type: string + required: true + description: 文件中的 `_id` 或者 `slug`. + +tags: + - name: "users" + +securityDefinitions: + basicAuth: + type: basic + description: HTTP基础授权。我们会提供你的客户ID(`CLIENT_ID`)和私密(`CLIENT_SECRET`)文件给你 + +security: + - basicAuth: [] + +definitions: + roleString: + type: string + description: 通常这是指`"teacher"`("老师")或者`"student"`("学生") + + datetimeString: + type: string + pattern: /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{3}Z$/ + + objectIdString: + type: string + pattern: /^[0-9a-f]{24}$/ + + UserResponse: + type: object + title: 'UserResponse (用户反馈结果)' + description: '这里所列类别的子选项' + properties: + _id: + $ref: '#/definitions/objectIdString' + email: + type: string + name: + type: string + slug: + type: string + role: + $ref: '#/definitions/roleString' + stats: + type: object + properties: + gamesCompleted: + type: number + concepts: + type: object + additionalProperties: + type: number + playTime: + type: number + description: '只有在终点询问的时候才包含进去' + oAuthIdentities: + type: array + items: + type: object + properties: + provider: + type: string + id: + type: string + subscription: + type: object + properties: + ends: + $ref: '#/definitions/datetimeString' + active: + type: 'boolean' + license: + type: object + properties: + ends: + $ref: '#/definitions/datetimeString' + active: + type: 'boolean' + + ClassroomResponse: + type: object + title: 'ClassroomResponse (教室反馈结果)' + description: '这里列出特征的子集' + properties: + _id: + $ref: '#/definitions/objectIdString' + name: + type: string + members: + type: array + items: + $ref: '#/definitions/objectIdString' + ownerID: + $ref: '#/definitions/objectIdString' + description: + type: string + courses: + type: array + items: + type: object + properties: + _id: + $ref: '#/definitions/objectIdString' + levels: + type: array + items: + type: object + enrolled: + type: array + items: + $ref: '#/definitions/objectIdString' + instance_id: + $ref: '#/definitions/objectIdString' + + ClassroomResponseWithCode: + type: object + title: 'ClassroomResponseWithCode (教室反馈结果代码)' + description: '这里列出属性的子集' + properties: + _id: + $ref: '#/definitions/objectIdString' + name: + type: string + members: + type: array + items: + $ref: '#/definitions/objectIdString' + ownerID: + $ref: '#/definitions/objectIdString' + description: + type: string + code: + type: string + codeCamel: + type: string + courses: + type: array + items: + type: object + properties: + _id: + $ref: '#/definitions/objectIdString' + levels: + type: array + items: + type: object + enrolled: + type: array + items: + $ref: '#/definitions/objectIdString' + instance_id: + $ref: '#/definitions/objectIdString' + clanId: + $ref: '#/definitions/objectIdString' + + + PlaytimeStatsResponse: + type: object + properties: + playTime: + type: number + description: "以秒统计游戏使用时间" + gamesPlayed: + type: number + description: "所玩的级别" + + LicenseStatsResponse: + type: object + properties: + licenseDaysGranted: + type: number + description: "授权订阅天数总数量" + licenseDaysUsed: + type: number + description: "订阅许可天数已使用数量" + licenseDaysRemaining: + type: number + description: "订阅许可天数剩余数量" + activeLicenses: + type: number + description: "活跃/有效订阅许可数量" + + LevelSessionResponse: + type: object + properties: + state: + type: object + properties: + complete: + type: boolean + level: + type: object + properties: + original: + type: string + description: '这个`id`代表这个水平' + levelID: + type: 'string' + description: '关卡名,如 wakka-maul' + creator: + type: 'string' + $ref: '#/definitions/objectIdString' + playtime: + type: 'integer' + description: '以秒为单位游戏所玩时间' + changed: + $ref: '#/definitions/datetimeString' + created: + $ref: '#/definitions/datetimeString' + dateFirstCompleted: + $ref: '#/definitions/datetimeString' + submitted: + type: 'boolean' + description: '对于竞技场来说,这一关是否已经加入进阶关卡。' + published: + type: 'boolean' + description: '分享的项目。这些项目是否已经分享给了同学。' + + ClanResponse: + type: object + title: 'ClanResponse' + description: '这里列出特征的子集' + properties: + _id: + $ref: '#/definitions/objectIdString' + name: + type: string + displayName: + type: string + members: + type: array + items: + $ref: '#/definitions/objectIdString' + ownerID: + $ref: '#/definitions/objectIdString' + description: + type: string + type: + type: string + kind: + type: string + metadata: + type: object \ No newline at end of file diff --git a/fern/api/generators.yml b/fern/apis/english/generators.yml similarity index 78% rename from fern/api/generators.yml rename to fern/apis/english/generators.yml index cc47f81..0b187ff 100644 --- a/fern/api/generators.yml +++ b/fern/apis/english/generators.yml @@ -21,14 +21,6 @@ groups: repository: codecombat/codecombat-python config: client_class_name: CodeCombat - - name: fernapi/fern-openapi - version: 0.0.19 - github: - repository: codecombat/codecombat-openapi - - name: fernapi/fern-openapi - version: 0.0.19 - github: - repository: codecombat/docs - name: fernapi/fern-postman version: 0.0.34 output: diff --git a/fern/apis/english/openapi/openapi.yml b/fern/apis/english/openapi/openapi.yml new file mode 100644 index 0000000..23ad602 --- /dev/null +++ b/fern/apis/english/openapi/openapi.yml @@ -0,0 +1,1026 @@ +swagger: "2.0" +info: + version: "0.0.0" + title: CodeCombat API + description: | + ## Basics + + * Examples are in JavaScript on a Node/Express server with [request](https://github.com/request/request) installed. + * Request and responses are in JSON. + * API responses are the base resource being created/referenced. So, for example, all routes starting with `/api/users` return [User](#users) resources. + + ## Client Setup + + We currently do not have a way for you to create or set up your own API Client or OAuth Provider information. Please contact us directly to get started. + + ## Client Authentication + + API routes must be called with Basic HTTP Authentication. You will receive a username (CLIENT_ID) and password (CLIENT_SECRET) upon creation of your API Client in our system. Provide those credentials with each API request. + + ```javascript + url = 'https://codecombat.com/api/users' + json = { name: 'A username' } + auth = { name: CLIENT_ID, pass: CLIENT_SECRET } + request.get({ url, json, auth }, (err, res) => console.log(res.statusCode, res.body)) + ``` + + We strongly recommend using a secrets manager for storing your client secret. Plain text files like dotenv lead to accidental, costly leaks. Use [Doppler](https://doppler.com/) for a developer-friendly experience. AWS and Google Cloud have native solutions as well. + + ## User Authentication + + To authenticate a user on CodeCombat through your service, you will need to use the below OAuth 2 process. CodeCombat will act as the client, and your service will act as the provider. + First, you will need to provide a trusted lookup URL and/or a token URL for the setup(See Client Setup above). Then the process from user account creation to log in will look like this: + + 1. **Create the user** using [POST /api/users](#users/post_users). + 1. **Link the CodeCombat user to an OAuth identity** using [POST /api/users/:handle/o-auth-identities](#users/post_users__handle__o_auth_identities). + You can call this API with a code or an access token. If no access token is given, we will use the token URL to exchange the given code for an access token. + Then we call the lookup URL with the access token to receive the user information (`id`) from your system which is saved to the user in our db. + 1. **Log the user in** by redirecting them to [/auth/login-o-auth](#auth/get_auth_login_o_auth). + You can call this API with the code/access token, and we will get the user information from your system similarly to step 2. + Finally, we match this information with what is stored in our database in step 2. If everything checks out, the user is logged in and redirected to the home page. + + There is also a [concrete example](https://s3.amazonaws.com/files.codecombat.com/codecombat_oauth_example.tar.gz) depicting the above process for better understanding. You can also refer to this [diagram](https://s3.amazonaws.com/files.codecombat.com/Example_OAuth_Flow.png). + +host: codecombat.com +basePath: /api +schemes: + - https +consumes: + - application/json +produces: + - application/json + +paths: + /auth/login-o-auth: + get: + summary: Login User + tags: + - auth + operationId: loginOauth + description: | + Logs a user in. + + In this example, we call your lookup URL (let's say, `https://oauth.provider/user?t=<%= accessToken %>`) with the access token (`1234`). The lookup URL returns `{ id: 'abcd' }` in this case. We will match this `id` with the OAuthIdentity stored in the user information in our db. If everything checks out, the user is logged in and redirected to the home page. + parameters: + - name: provider + in: query + required: true + type: string + description: Your OAuth Provider ID + - name: accessToken + in: query + type: string + description: Will be passed through your lookup URL to get the user ID. Required if no `code`. + - name: code + type: string + in: query + description: Will be passed to the OAuth token endpoint to get a token. Required if no `accessToken`. + - name: redirect + type: string + in: query + description: Override where the user will navigate to after successfully logging in. + - name: errorRedirect + type: string + in: query + description: If an error happens, redirects the user to this url, with at least query parameters `code`, `errorName` and `message`. + responses: + "302": + description: 'Redirects the user to a landing page after having logged them in.' + + /users: + post: + summary: Create User + tags: + - users + operationId: users-create + description: | + Creates a `User`. + parameters: + - name: user + in: body + schema: + type: object + required: + - name + - email + properties: + name: + type: string + email: + type: string + role: + type: string + description: | + `"student"` or `"teacher"`. If unset, a home user will be created, unable to join classrooms. + enum: + - student + - teacher + preferredLanguage: + type: string + heroConfig: + type: object + properties: + thangType: + $ref: '#/definitions/objectIdString' + birthday: + type: string + responses: + "201": + description: 'The created user' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}: + get: + summary: Get User + tags: + - users + operationId: users-get + description: Returns a `User`. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: includePlayTime + in: query + description: "Set to non-empty string to include stats.playTime in response" + required: false + type: string + responses: + "200": + description: 'The requested user' + schema: + $ref: '#/definitions/UserResponse' + + put: + summary: Update User + tags: + - users + operationId: users-update + description: Modify name of a `User` + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: user + in: body + required: true + schema: + type: object + required: + - name + properties: + name: + type: string + description: 'Set to new name string' + birthday: + type: string + description: 'Set the birthday' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + + /users/{handle}/classrooms: + get: + summary: Get Classrooms By User + tags: + - users + operationId: users-get-classrooms + description: Returns a list of `Classrooms` this user is in (if a student) or owns (if a teacher). + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: retMemberLimit + in: query + description: "limit the return number of members for each classroom" + required: false + type: number + responses: + "200": + description: 'The requested classrooms' + schema: + type: array + items: + $ref: '#/definitions/ClassroomResponseWithCode' + + /users/{handle}/hero-config: + put: + summary: Get User Hero + tags: + - users + operationId: users-set-hero + description: Set the user's hero. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: heroConfig + in: body + schema: + type: object + properties: + thangType: + $ref: '#/definitions/objectIdString' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/ace-config: + put: + summary: Put Ace Config + tags: + - users + operationId: users-set-ace-config + description: Set the user's aceConfig (the settings for the in-game Ace code editor), such as whether to enable autocomplete. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: aceConfig + in: body + schema: + type: object + properties: + liveCompletion: + type: boolean + required: false + description: 'controls whether autocompletion snippets show up, the default value is true' + behaviors: + type: boolean + required: false + description: 'controls whether things like automatic parenthesis and quote completion happens, the default value is false' + language: + type: string + required: false + description: 'only for home users, should be one of ["python", "javascript", "cpp", "lua", "coffeescript"] right now' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + + /users/{handle}/o-auth-identities: + post: + tags: + - users + summary: Add Oauth2 Identity + description: | + Adds an OAuth2 identity to the user, so that they can be logged in with that identity. You need to send the OAuth code or the access token to this endpoint. + + 1. If no access token is provided, it will use your OAuth2 token URL to exchange the given code for an access token. + 2. Then it will use the access token (given by you, or received from step 1) to look up the user on your service using the lookup URL, and expects a JSON object in response with an `id` property. + 3. It will then save that user `id` to the user in our db as a new OAuthIdentity. + + In this example, we call your lookup URL (let's say, `https://oauth.provider/user?t=<%= accessToken %>`) with the access token (`1234`). The lookup URL returns `{ id: 'abcd' }` in this case, which we save to the user in our db. + + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: oAuthIdentity + in: body + required: true + schema: + type: object + required: + - provider + properties: + provider: + type: string + description: Your OAuth Provider ID. + accessToken: + type: string + description: Will be passed through your lookup URL to get the user ID. Required if no `code`. + code: + type: string + description: Will be passed to the OAuth token endpoint to get a token. Required if no `accessToken`. + + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/subscription: + put: + tags: + - users + summary: Put Subscription + operationId: users-grant-premium-subscription + description: | + Grants a user premium access to the "Home" version up to a certain time. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: subscription + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/shorten-subscription: + put: + tags: + - users + summary: Shorten User Subscription + operationId: users-shorten-subscription + description: | + If the user already has a premium access up to a certain time, this shortens/revokes his/her premium access. + If the ends is less than or equal to the current time, it revokes the subscription and sets the end date to be the current time, else it just shortens the subscription. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: subscription + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/license: + put: + tags: + - users + summary: Grant User License + operationId: users-grant-license + description: | + Grants a user access to the "Classroom" version up to a certain time. + Sets their role to "student". + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: license + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + /users/{handle}/shorten-license: + put: + summary: Shorten User License + tags: + - users + operationId: users-shorten-license + description: | + If the user already has access to the "Classroom" version up to a certain time, this shortens/revokes his/her access. + If the ends is less than or equal to the current time, it revokes the enrollment and sets the end date to be the current time, else it just shortens the enrollment. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: license + in: body + required: true + schema: + type: object + required: + - ends + properties: + ends: + $ref: '#/definitions/datetimeString' + responses: + "200": + description: 'The affected user' + schema: + $ref: '#/definitions/UserResponse' + + /clan/{handle}/members: + put: + tags: + - clans + summary: Upsert User Into Clan + operationId: clans-upsert-member + description: Upserts a user into the clan. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: member + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + description: "The `_id` or `slug` of the user to add to the clan." + type: string + responses: + "200": + description: 'The clan with the member added.' + schema: + $ref: '#/definitions/ClanResponse' + + /classrooms: + post: + tags: + - classrooms + summary: Create a classroom + operationId: classrooms-create + description: Creates a new empty `Classroom`. + parameters: + - name: classroom + in: body + required: true + schema: + type: object + required: + - name + - ownerID + - aceConfig + properties: + name: + type: string + description: Name of the classroom + ownerID: + $ref: '#/definitions/objectIdString' + description: The `_id` of the teacher for whom classroom is being created + aceConfig: + type: object + properties: + language: + type: string + description: Programming language for the classroom + responses: + "201": + description: 'The created classroom' + schema: + $ref: '#/definitions/ClassroomResponseWithCode' + + get: + summary: Get Classroom Details + tags: + - classrooms + operationId: classrooms-get + description: Returns the classroom details for a class code. + parameters: + - name: code + in: query + type: string + required: true + description: The classroom's `code`. + - name: retMemberLimit + in: query + description: "limit the return number of members for the classroom" + required: false + type: number + responses: + "200": + description: 'The classroom details.' + schema: + $ref: '#/definitions/ClassroomResponseWithCode' + + /classrooms/{handle}/members: + put: + tags: + - classrooms + operationId: classrooms-upsert-member + summary: Upsert a user from classroom + description: Upserts a user into the classroom. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: member + in: body + required: true + schema: + type: object + required: + - code + - userId + properties: + code: + type: string + description: "The code for joining this classroom" + userId: + description: "The `_id` or `slug` of the user to add to the class." + type: string + retMemberLimit: + description: "limit the return number of members for the classroom, the default value is 1000" + type: number + responses: + "200": + description: 'The classroom with the member added.' + schema: + $ref: '#/definitions/ClassroomResponse' + delete: + summary: Delete User from Classroom + tags: + - classrooms + operationId: classrooms-remove-member + description: Remove a user from the classroom. + parameters: + - $ref: '#/parameters/handlePathParameter' + - name: member + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + description: "The `_id` or `slug` of the user to remove from the class." + type: string + retMemberLimit: + description: "limit the return number of members for the classroom, the default value is 1000" + type: number + responses: + "200": + description: 'The classroom with the member removed.' + schema: + $ref: '#/definitions/ClassroomResponse' + + + /classrooms/{classroomHandle}/courses/{courseHandle}/enrolled: + put: + summary: Enroll User in a Course + tags: + - classrooms + operationId: classrooms-enroll-user-in-course + description: | + Enrolls a user in a course in a classroom. + If the course is paid, user must have an active license. + User must be a member of the classroom. + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: The classroom's `_id`. + - name: courseHandle + in: path + type: string + required: true + description: The course's `_id`. + - name: userId + description: "The `_id` or `slug` of the user to add to the class." + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + $ref: '#/definitions/objectIdString' + - name: retMemberLimit + in: query + description: "limit the return number of members for the classroom, the default value is 1000" + required: false + type: number + + responses: + "200": + description: 'The classroom with the user enrolled.' + schema: + $ref: '#/definitions/ClassroomResponse' + + /classrooms/{classroomHandle}/courses/{courseHandle}/remove-enrolled: + put: + summary: Remove User from a classroom + tags: + - classrooms + operationId: classrooms-remove-enrolled-user + description: | + Removes an enrolled user from a course in a classroom. + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: The classroom's `_id`. + - name: courseHandle + in: path + type: string + required: true + description: The course's `_id`. + - name: userId + description: "The `_id` or `slug` of the user to remove from the course." + in: body + required: true + schema: + type: object + required: + - userId + properties: + userId: + $ref: '#/definitions/objectIdString' + - name: retMemberLimit + in: query + description: "limit the return number of members for the classroom, the default value is 1000" + required: false + type: number + responses: + "200": + description: 'The classroom with the user removed from the course.' + schema: + $ref: '#/definitions/ClassroomResponse' + + /classrooms/{classroomHandle}/stats: + get: + summary: Get Member Stats + tags: + - classrooms + operationId: classrooms-get-members-stats + description: | + Returns a list of all members stats for the classroom. + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: The classroom's `_id`. + - name: project + in: query + type: string + description: | + If specified, include only the specified projection of returned stats; else, return all stats. Format as a comma-separated list, like `creator,playtime,state.complete`. + required: false + - name: memberLimit + in: query + type: number + description: 'Limit the return member number. the default value is 10, and the max value is 100' + required: false + - name: memberSkip + in: query + type: number + description: | + Skip the members that doesn't need to return, for pagination + required: false + + responses: + "200": + description: 'The members stats for the classroom.' + schema: + type: array + items: + type: object + properties: + _id: + $ref: '#/definitions/objectIdString' + stats: + type: object + properties: + gamesCompleted: + type: number + playtime: + type: number + description: "Total play time in seconds" + + /classrooms/{classroomHandle}/members/{memberHandle}/sessions: + get: + summary: Get Level Session + tags: + - classrooms + operationId: classrooms-get-levels-played + description: | + Returns a list of all levels played by the user for the classroom. + parameters: + - name: classroomHandle + in: path + type: string + required: true + description: The classroom's `_id`. + - name: memberHandle + in: path + type: string + required: true + description: The classroom member's `_id`. + responses: + "200": + description: 'The classroom with the user enrolled.' + schema: + type: array + items: + $ref: '#/definitions/LevelSessionResponse' + + /user-lookup/{property}/{value}: + get: + summary: Search for User + tags: + - users + operationId: users-lookup + description: Redirects to `/users/{handle}` given a unique, identifying property + parameters: + - name: property + in: path + type: string + required: true + description: The property to lookup by. May either be `"israel-id"` or `"name"`. + - name: value + in: path + type: string + required: true + description: The value to be looked up. + responses: + "301": + description: "Redirect to `User` resource at `/users/{handle}`" + + /playtime-stats: + get: + summary: Get Playtime Stats + tags: + - stats + operationId: stats-get-playtime-stats + description: Returns the playtime stats + parameters: + - name: startDate + in: query + description: "Earliest an included user was created" + required: false + type: string + - name: endDate + in: query + description: "Latest an included user was created" + required: false + type: string + - name: country + in: query + description: "Filter by country string" + required: false + type: string + + responses: + "200": + description: "Returns the playtime stats accross all owned users." + schema: + $ref: '#/definitions/PlaytimeStatsResponse' + + /license-stats: + get: + summary: Get License Stats + tags: + - stats + operationId: stats-get-license-stats + description: Returns the license stats + + responses: + "200": + description: "Returns the license stats for classroom/home subscription licenses." + schema: + $ref: '#/definitions/LicenseStatsResponse' + +parameters: + handlePathParameter: + name: handle + in: path + type: string + required: true + description: The document's `_id` or `slug`. + +tags: + - name: "users" + +securityDefinitions: + basicAuth: + type: basic + description: HTTP Basic Authentication. We will need to provide you with your client ID and secret. + +security: + - basicAuth: [] + +definitions: + roleString: + type: string + description: "Usually either 'teacher' or 'student'" + + datetimeString: + type: string + pattern: /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{3}Z$/ + + objectIdString: + type: string + pattern: /^[0-9a-f]{24}$/ + + UserResponse: + type: object + title: 'UserResponse' + description: 'Subset of properties listed here' + properties: + _id: + $ref: '#/definitions/objectIdString' + email: + type: string + name: + type: string + slug: + type: string + role: + $ref: '#/definitions/roleString' + stats: + type: object + properties: + gamesCompleted: + type: number + concepts: + type: object + additionalProperties: + type: number + playTime: + type: number + description: 'Included only when specifically requested on the endpoint' + oAuthIdentities: + type: array + items: + type: object + properties: + provider: + type: string + id: + type: string + subscription: + type: object + properties: + ends: + $ref: '#/definitions/datetimeString' + active: + type: 'boolean' + license: + type: object + properties: + ends: + $ref: '#/definitions/datetimeString' + active: + type: 'boolean' + + ClassroomResponse: + type: object + title: 'ClassroomResponse' + description: 'Subset of properties listed here' + properties: + _id: + $ref: '#/definitions/objectIdString' + name: + type: string + members: + type: array + items: + $ref: '#/definitions/objectIdString' + ownerID: + $ref: '#/definitions/objectIdString' + description: + type: string + courses: + type: array + items: + type: object + properties: + _id: + $ref: '#/definitions/objectIdString' + levels: + type: array + items: + type: object + enrolled: + type: array + items: + $ref: '#/definitions/objectIdString' + instance_id: + $ref: '#/definitions/objectIdString' + + ClassroomResponseWithCode: + type: object + title: 'ClassroomResponseWithCode' + description: 'Subset of properties listed here' + properties: + _id: + $ref: '#/definitions/objectIdString' + name: + type: string + members: + type: array + items: + $ref: '#/definitions/objectIdString' + ownerID: + $ref: '#/definitions/objectIdString' + description: + type: string + code: + type: string + codeCamel: + type: string + courses: + type: array + items: + type: object + properties: + _id: + $ref: '#/definitions/objectIdString' + levels: + type: array + items: + type: object + enrolled: + type: array + items: + $ref: '#/definitions/objectIdString' + instance_id: + $ref: '#/definitions/objectIdString' + clanId: + $ref: '#/definitions/objectIdString' + + PlaytimeStatsResponse: + type: object + properties: + playTime: + type: number + description: "Total play time in seconds" + gamesPlayed: + type: number + description: "Number of levels played" + + LicenseStatsResponse: + type: object + properties: + licenseDaysGranted: + type: number + description: "Total number of license days granted" + licenseDaysUsed: + type: number + description: "Number of license days used" + licenseDaysRemaining: + type: number + description: "Number of license days remaining" + activeLicenses: + type: number + description: "Number of active/valid licenses" + + LevelSessionResponse: + type: object + properties: + state: + type: object + properties: + complete: + type: boolean + level: + type: object + properties: + original: + type: string + description: 'The id for the level.' + levelID: + type: 'string' + description: 'Level slug like `wakka-maul`' + creator: + type: 'string' + $ref: '#/definitions/objectIdString' + playtime: + type: 'integer' + description: 'Time played in seconds.' + changed: + $ref: '#/definitions/datetimeString' + created: + $ref: '#/definitions/datetimeString' + dateFirstCompleted: + $ref: '#/definitions/datetimeString' + submitted: + type: 'boolean' + description: 'For arenas. Whether or not the level has been added to the ladder.' + published: + type: 'boolean' + description: 'For shareable projects. Whether or not the project has been shared with classmates.' + + ClanResponse: + type: object + title: 'ClanResponse' + description: 'Subset of properties listed here' + properties: + _id: + $ref: '#/definitions/objectIdString' + name: + type: string + displayName: + type: string + members: + type: array + items: + $ref: '#/definitions/objectIdString' + ownerID: + $ref: '#/definitions/objectIdString' + description: + type: string + type: + type: string + kind: + type: string + metadata: + type: object \ No newline at end of file diff --git a/fern/assets/favicon.png b/fern/assets/favicon.png new file mode 100644 index 0000000..2eff160 Binary files /dev/null and b/fern/assets/favicon.png differ diff --git a/fern/assets/logo.png b/fern/assets/logo.png new file mode 100644 index 0000000..a278961 Binary files /dev/null and b/fern/assets/logo.png differ diff --git a/fern/docs.yml b/fern/docs.yml new file mode 100644 index 0000000..6df93a1 --- /dev/null +++ b/fern/docs.yml @@ -0,0 +1,39 @@ +instances: + - url: codecombat.docs.buildwithfern.com + +navigation: + - version: English + layout: + - section: Overview + contents: + - page: Introduction + path: ./intro-en.mdx + - api-name: english + api: API Reference + - version: Chinese + layout: + - section: 概述 + contents: + - page: 介绍 + path: ./intro-ch.mdx + - api-name: chinese + api: API 参考 + +title: CodeCombat | API Reference +colors: + accentPrimary: + dark: "#1FBAB4" + light: "#63DFDA" +logo: + dark: ./assets/logo.png + light: ./assets/logo.png + height: 40 + href: https://codecombat.com +favicon: ./assets/favicon.png +navbar-links: + - type: secondary + text: Contact us + url: "mailto:support@codecombat.com" + - type: primary + text: Sign up + url: https://codecombat.com/ \ No newline at end of file diff --git a/fern/fern.config.json b/fern/fern.config.json index 5586432..ae5589b 100644 --- a/fern/fern.config.json +++ b/fern/fern.config.json @@ -1,4 +1,4 @@ { "organization": "codecombat", - "version": "0.6.10" + "version": "0.15.0-rc1" } \ No newline at end of file diff --git a/fern/intro-ch.mdx b/fern/intro-ch.mdx new file mode 100644 index 0000000..2d26ca8 --- /dev/null +++ b/fern/intro-ch.mdx @@ -0,0 +1,31 @@ +## 基本情况 + +* 开发者在Node/Express服务器上安装了以下 [请求](https://github.com/request/request),示例采用JavaScript形式。 +* 请求和反馈形式为JSON。 +* API接口是创建或者参考的基础资源。因此,举例来说,所有的开发路径(routes)是以'/api/users'开始,返回[User](#users)开发资源。 + + ## 客户设置 +目前我们没有让您创建或者设置您自己的客户级开发者接口(API Client)或者OAuth提供者(OAuth Privider)信息。请直接联系我们启动您的开发进程。 + +## 用户授权 +系统必须通过基础HTTP授权(Basic HTTP Authentication)来启动API路径。在我们的系统中创建你的API客户(API_CLIENT)之后,你会获得一个用户名(CLIENT_ID)和密码(CLIENT_SECRET)。对于每一个API请求,请提供你的姓名(CLIENT_ID)和密码(CLIENT_SECRET)。 + +```javascript +url = 'https://koudashijie.com/api/users' +json = { name: 'A username' } +auth = { name: CLIENT_ID, pass: CLIENT_SECRET } +request.get({ url, json, auth }, (err, res) => console.log(res.statusCode, res.body)) +``` + +## 用户授权 +通过您的服务在CodeCombat平台上认证一个用户,您需要采用以下oAuth 2流程. CodeCombat作为一个客户,您的服务作为服务提供者。首先,您需要提供一个可信的查找网址(lookup URL 或者 token URL),来进行创建设置(参见以上客户创建说明),这个创建账户和登陆的过程如以下所示: + +1. **创建用户** +2. **把CodeCombat用户和一个oAuth身份对接** + 你可以使用代码或者准入指令(access token)来启动这个API嵌入。如果你没有获得准入指令(access token),我们会用这个URL指令来替换原有的代码来获得一个准入指令(access token)。 + 然后我们用准入指令(access token)来启动查找网址(lookup URL),以此从你的系统中获得用户信息(`id`),这些信息已被储存在我们的用户数据库中。 +3. **用户登陆** + 你可以用这个代码/准入指令(access token)来call API, 然后我们会通过类似于第二步中的步骤来获得用户信息。 + 最后,我们将数据库在第二步中获得的信息与这个信息进行匹配,如果匹配成功,用户即可完成登录并被引导到主页。 + +这里还有一个描述以上过程的 [实例](https://s3.amazonaws.com/files.codecombat.com/codecombat_oauth_example.tar.gz),以便理解。 同时,你也可以参考这个 [图表](https://s3.amazonaws.com/files.codecombat.com/Example_OAuth_Flow.png)。 \ No newline at end of file diff --git a/fern/intro-en.mdx b/fern/intro-en.mdx new file mode 100644 index 0000000..b8b30df --- /dev/null +++ b/fern/intro-en.mdx @@ -0,0 +1,58 @@ +## Basics + +- Examples are in JavaScript on a Node/Express server with + [request](https://github.com/request/request) installed. +- Request and responses are in JSON +- API responses are the base resource being created/referenced. So, for example, + all routes starting with `/api/users` return [User](/users/post-users) + resources. + +## Client Setup + +We currently do not have a way for you to create or set up your own API Client +or OAuth Provider information. Please contact us directly to get started. + +## Client Authentication + +API routes must be called with Basic HTTP Authentication. You will receive a +username (CLIENT_ID) and password (CLIENT_SECRET) upon creation of your API +Client in our system. Provide those credentials with each API request. + +```javascript +url = "https://codecombat.com/api/users"; +json = { name: "A username" }; +auth = { name: CLIENT_ID, pass: CLIENT_SECRET }; +request.get({ url, json, auth }, (err, res) => + console.log(res.statusCode, res.body) +); +``` + +We strongly recommend using a secrets manager for storing your client secret. +Plain text files like dotenv lead to accidental, costly leaks. Use +[Doppler](https://www.doppler.com/) for a developer-friendly experience. AWS and +Google Cloud have native solutions as well. + +## User Authentication + +To authenticate a user on CodeCombat through your service, you will need to use +the below OAuth 2 process. CodeCombat will act as the client, and your service +will act as the provider. First, you will need to provide a trusted lookup URL +and/or a token URL for the setup(See Client Setup above). Then the process from +user account creation to log in will look like this: + +1. **Create the user** +2. **Link the CodeCombat user to an OAuth identity** + You can call this API with a code or an access token. If no access token is + given, we will use the token URL to exchange the given code for an access + token. Then we call the lookup URL with the access token to receive the user + information (id) from your system which is saved to the user in our db. +3. **Log the user in** + You can call this API with the code/access token, and we will get the user + information from your system similarly to step 2. Finally, we match this + information with what is stored in our database in step 2. If everything + checks out, the user is logged in and redirected to the home page. + +There is also a +[concrete example](https://s3.amazonaws.com/files.codecombat.com/codecombat_oauth_example.tar.gz) +depicting the above process for better understanding. You can also refer to this +[diagram](https://s3.amazonaws.com/files.codecombat.com/Example_OAuth_Flow.png).