mirror of
https://github.com/valitydev/swag-organizations.git
synced 2024-11-06 01:55:22 +00:00
CAPI-421: Draft initial spec (#1)
This commit is contained in:
parent
072c14f769
commit
d8199c8f85
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,7 +38,6 @@ node_modules
|
||||
|
||||
# Generated
|
||||
web_deploy/
|
||||
package-lock.json
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "build-utils"]
|
||||
path = build-utils
|
||||
url = git@github.com:rbkmoney/build_utils
|
23
.redocly.yaml
Normal file
23
.redocly.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
# See https://docs.redoc.ly/cli/configuration/ for more information.
|
||||
apiDefinitions:
|
||||
binbase: "openapi/openapi.yaml"
|
||||
lint:
|
||||
rules:
|
||||
api-servers: on
|
||||
camel-case-names: on
|
||||
model-description: on
|
||||
no-extra-fields: on
|
||||
operation-operationId: on
|
||||
operation-description: on
|
||||
operation-tags: on
|
||||
parameter-description: on
|
||||
path-declarations-must-exist: on
|
||||
path-keys-no-trailing-slash: on
|
||||
server-not-example: on
|
||||
servers-no-trailing-slash: on
|
||||
referenceDocs:
|
||||
showConsole: true
|
||||
layout:
|
||||
scope: section
|
||||
routingStrategy: browser
|
||||
htmlTemplate: ./web/index.html
|
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
||||
* @rbkmoney/frontend
|
29
Jenkinsfile
vendored
Normal file
29
Jenkinsfile
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
#!groovy
|
||||
// -*- mode: groovy -*-
|
||||
|
||||
build('swag-organizations', 'docker-host') {
|
||||
checkoutRepo()
|
||||
loadBuildUtils('build-utils')
|
||||
|
||||
def pipeDefault
|
||||
def withWsCache
|
||||
runStage('load pipeline') {
|
||||
env.JENKINS_LIB = "build-utils/jenkins_lib"
|
||||
pipeDefault = load("${env.JENKINS_LIB}/pipeDefault.groovy")
|
||||
withWsCache = load("${env.JENKINS_LIB}/withWsCache.groovy")
|
||||
}
|
||||
|
||||
pipeDefault() {
|
||||
|
||||
runStage('install-deps') {
|
||||
withWsCache("node_modules") {
|
||||
sh 'make wc_install'
|
||||
}
|
||||
}
|
||||
|
||||
runStage('validate-spec') {
|
||||
sh 'make wc_validate'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
20
Makefile
Normal file
20
Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
UTILS_PATH := build-utils
|
||||
TEMPLATES_PATH := .
|
||||
|
||||
SERVICE_NAME := swag-organizations
|
||||
BUILD_IMAGE_TAG := 442c2c274c1d8e484e5213089906a4271641d95e
|
||||
|
||||
CALL_ANYWHERE := all install validate
|
||||
CALL_W_CONTAINER := $(CALL_ANYWHERE)
|
||||
|
||||
all: validate
|
||||
|
||||
-include $(UTILS_PATH)/make_lib/utils_container.mk
|
||||
|
||||
.PHONY: $(CALL_W_CONTAINER)
|
||||
|
||||
install:
|
||||
npm install
|
||||
|
||||
validate:
|
||||
npm run validate
|
17
TODO.md
Normal file
17
TODO.md
Normal file
@ -0,0 +1,17 @@
|
||||
## Нужно сделать
|
||||
|
||||
* Описания разделов API.
|
||||
* Конструирование пользовательских ролей из разрешений.
|
||||
* Возможность редактировать администраторами user email сотрудников.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
1. Если у меня как у сотрудника нет прав на просмотр сотрудников, могу ли я видеть идентификатор владельца?
|
||||
|
||||
1. Идентификатор сотрудника **равен** идентификатору пользователя, это ок?
|
||||
|
||||
В такой схеме есть небольшие утечки данных, вроде той, когда я как администратор могу понять, что пользователь состоит в тех же организациях, что и я. Непонятно, насколько это критично.
|
||||
|
||||
1. Если мы научимся 🌚 удалять магазины, как следить за консистентностью скоупов?
|
||||
|
||||
1. Ради простоты мы можем приравнять отзыв, принятие и протухание приглашения к его удалению, а по факту отзыва и принятия просто рассылать письма администраторам. Это избавит нас от необходимости держать все существовавшие когда-либо пришлашения в базе, и следить только за списком активных приглашений. Однако это может быть не очень дружественно к пользователям. Можем ли мы себе позволить это упрощение?
|
1
build-utils
Submodule
1
build-utils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 39115931c6559011bf2f2ed353087976e6a7a5e7
|
31
build.js
Normal file
31
build.js
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
var Path = require('path');
|
||||
|
||||
var TARGET_DIR = 'dist'
|
||||
if (process.argv[2]) {
|
||||
TARGET_DIR = process.argv[2]
|
||||
}
|
||||
|
||||
require('shelljs/global');
|
||||
set('-e');
|
||||
|
||||
mkdir('-p', TARGET_DIR)
|
||||
|
||||
cp('-R', 'web/*', TARGET_DIR + '/');
|
||||
|
||||
exec('npm run openapi bundle -- -o ' + TARGET_DIR + ' --ext json');
|
||||
exec('npm run openapi bundle -- -o ' + TARGET_DIR + ' --ext yaml');
|
||||
|
||||
exec([
|
||||
'npm run redoc bundle --',
|
||||
'--cdn',
|
||||
'-o', Path.join(TARGET_DIR, 'index.html'),
|
||||
Path.join(TARGET_DIR, 'openapi.yaml')
|
||||
].join(' '));
|
||||
|
||||
var SWAGGER_UI_DIST = Path.dirname(require.resolve('swagger-ui-dist'));
|
||||
var SWAGGER_UI_DIR = Path.join(TARGET_DIR, 'swagger-ui');
|
||||
rm('-rf', SWAGGER_UI_DIR);
|
||||
cp('-R', SWAGGER_UI_DIST, SWAGGER_UI_DIR);
|
||||
sed('-i', 'https://petstore.swagger.io/v2/swagger.json', '../openapi.json', TARGET_DIR + '/swagger-ui/index.html');
|
10
openapi/components/parameters/idempotencyKey.yaml
Normal file
10
openapi/components/parameters/idempotencyKey.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
name: X-Idempotency-Key
|
||||
in: header
|
||||
description: |
|
||||
Уникальный ключ для обеспечения единоразовой (идемпотентной) обработки операции.
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
minLength: 4
|
||||
maxLength: 64
|
||||
example: 881D:08BA
|
6
openapi/components/parameters/invitationId.yaml
Normal file
6
openapi/components/parameters/invitationId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: invitationId
|
||||
in: path
|
||||
description: Идентификатор приглашения
|
||||
required: true
|
||||
schema:
|
||||
$ref: '../schemas/InvitationId.yaml'
|
6
openapi/components/parameters/orgId.yaml
Normal file
6
openapi/components/parameters/orgId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: orgId
|
||||
in: path
|
||||
description: Идентификатор организации
|
||||
required: true
|
||||
schema:
|
||||
$ref: '../schemas/OrganizationId.yaml'
|
9
openapi/components/parameters/requestId.yaml
Normal file
9
openapi/components/parameters/requestId.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
name: X-Request-ID
|
||||
in: header
|
||||
description: Уникальный идентификатор запроса к системе
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
minLength: 4
|
||||
maxLength: 64
|
||||
example: RQID-Z08G3EFE5DZ429VVO755BM19D51
|
6
openapi/components/parameters/roleId.yaml
Normal file
6
openapi/components/parameters/roleId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: roleId
|
||||
in: path
|
||||
description: Идентификатор роли
|
||||
required: true
|
||||
schema:
|
||||
$ref: '../schemas/RoleId.yaml'
|
6
openapi/components/parameters/userId.yaml
Normal file
6
openapi/components/parameters/userId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: userId
|
||||
in: path
|
||||
description: Идентификатор пользователя
|
||||
required: true
|
||||
schema:
|
||||
$ref: '../schemas/UserId.yaml'
|
15
openapi/components/responses/BadRequest.yaml
Normal file
15
openapi/components/responses/BadRequest.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
description: Переданы ошибочные данные
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
description: Ошибка в переданных данных
|
||||
type: object
|
||||
required:
|
||||
- code
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- invalidRequest
|
||||
message:
|
||||
type: string
|
33
openapi/components/schemas/Invitation.yaml
Normal file
33
openapi/components/schemas/Invitation.yaml
Normal file
@ -0,0 +1,33 @@
|
||||
description: Приглашение для сотрудника
|
||||
allOf:
|
||||
- type: object
|
||||
required:
|
||||
- id
|
||||
- createdAt
|
||||
- expiresAt
|
||||
properties:
|
||||
id:
|
||||
$ref: './InvitationId.yaml'
|
||||
createdAt:
|
||||
description: Дата и время создания
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
expiresAt:
|
||||
description: Дата и время истечения срока принятия
|
||||
type: string
|
||||
format: date-time
|
||||
invitee:
|
||||
$ref: './Invitee.yaml'
|
||||
acceptToken:
|
||||
allOf:
|
||||
- $ref: './InvitationAcceptToken.yaml'
|
||||
- readOnly: true
|
||||
metadata:
|
||||
description: |
|
||||
Произвольный, специфичный для клиента API и непрозрачный для системы набор данных, ассоциированных с
|
||||
данным приглашением
|
||||
type: object
|
||||
example:
|
||||
mailTemplate: 'v2.beta'
|
||||
- $ref: './InvitationStatus.yaml'
|
7
openapi/components/schemas/InvitationAcceptToken.yaml
Normal file
7
openapi/components/schemas/InvitationAcceptToken.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 4000
|
||||
description:
|
||||
Токен для принятия приглашения пользователем
|
||||
example: |
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsYXVyYS5wYWxtZXJAbmlpZGFyLnN1IiwiaWF0IjoxNTE2MjM5MDIyLCJvcmciOiJvcl9hZjllNzZ1YzViNDdoOGIxNTQuMTliOHhhNjFkYzk0Iiwicm9sZXMiOltdfQ.p0kQj5AuogXuBwBvAFWdk8beI5JThXksRH7kFUjvRMk
|
16
openapi/components/schemas/InvitationAccepted.yaml
Normal file
16
openapi/components/schemas/InvitationAccepted.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
allOf:
|
||||
- $ref: './Invitation.yaml'
|
||||
- type: object
|
||||
properties:
|
||||
acceptedAt:
|
||||
description: Дата и время принятия приглашения
|
||||
type: string
|
||||
format: date-time
|
||||
member:
|
||||
description: Сотрудник организации, принявший приглашение
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
$ref: './UserId.yaml'
|
2
openapi/components/schemas/InvitationExpired.yaml
Normal file
2
openapi/components/schemas/InvitationExpired.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
allOf:
|
||||
- $ref: './Invitation.yaml'
|
6
openapi/components/schemas/InvitationId.yaml
Normal file
6
openapi/components/schemas/InvitationId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
description: Идентификатор приглашения
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
example: 1KgIYBGsCgq
|
||||
readOnly: true
|
2
openapi/components/schemas/InvitationPending.yaml
Normal file
2
openapi/components/schemas/InvitationPending.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
allOf:
|
||||
- $ref: './Invitation.yaml'
|
12
openapi/components/schemas/InvitationRevoked.yaml
Normal file
12
openapi/components/schemas/InvitationRevoked.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
allOf:
|
||||
- $ref: './Invitation.yaml'
|
||||
- type: object
|
||||
properties:
|
||||
revokedAt:
|
||||
description: Дата и время отзыва приглашения
|
||||
type: string
|
||||
format: date-time
|
||||
reason:
|
||||
description: Причина отзыва приглашения
|
||||
type: string
|
||||
maxLength: 1000
|
14
openapi/components/schemas/InvitationStatus.yaml
Normal file
14
openapi/components/schemas/InvitationStatus.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
discriminator:
|
||||
propertyName: status
|
||||
mapping:
|
||||
'Pending': './InvitationPending.yaml'
|
||||
'Accepted': './InvitationAccepted.yaml'
|
||||
'Expired': './InvitationExpired.yaml'
|
||||
'Revoked': './InvitationRevoked.yaml'
|
||||
properties:
|
||||
status:
|
||||
description: Статус приглашения
|
||||
readOnly: true
|
7
openapi/components/schemas/InvitationStatusName.yaml
Normal file
7
openapi/components/schemas/InvitationStatusName.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
type: string
|
||||
enum:
|
||||
- 'Pending'
|
||||
- 'Accepted'
|
||||
- 'Expired'
|
||||
- 'Revoked'
|
||||
default: 'Pending'
|
23
openapi/components/schemas/Invitee.yaml
Normal file
23
openapi/components/schemas/Invitee.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
description: Приглашаемый пользователь
|
||||
type: object
|
||||
required:
|
||||
- contact
|
||||
- roles
|
||||
properties:
|
||||
contact:
|
||||
description: Контактные данные для отправки приглашения
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- email
|
||||
properties:
|
||||
type:
|
||||
enum:
|
||||
- EMail
|
||||
email:
|
||||
$ref: './UserEmail.yaml'
|
||||
roles:
|
||||
allOf:
|
||||
- description: |
|
||||
Роли, которые будут назначены сотруднику после принятия им приглашения
|
||||
- $ref: './MemberRoleSet.yaml'
|
13
openapi/components/schemas/Member.yaml
Normal file
13
openapi/components/schemas/Member.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
description: Сотрудник организации
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- userEmail
|
||||
- roles
|
||||
properties:
|
||||
id:
|
||||
$ref: './UserId.yaml'
|
||||
userEmail:
|
||||
$ref: './UserEmail.yaml'
|
||||
roles:
|
||||
$ref: './MemberRoleSet.yaml'
|
22
openapi/components/schemas/MemberRole.yaml
Normal file
22
openapi/components/schemas/MemberRole.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
description: Роль сотрудника в организации
|
||||
type: object
|
||||
required:
|
||||
- roleId
|
||||
properties:
|
||||
roleId:
|
||||
$ref: './RoleId.yaml'
|
||||
scope:
|
||||
description: Область действия этой роли
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- resourceId
|
||||
properties:
|
||||
id:
|
||||
$ref: './ResourceScopeId.yaml'
|
||||
resourceId:
|
||||
description: Идентификатор доступного в рамках области ресурса
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
example: sh_27839dt482v28ab7escze113y858dp
|
6
openapi/components/schemas/MemberRoleSet.yaml
Normal file
6
openapi/components/schemas/MemberRoleSet.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
description: Набор ролей сотрудника
|
||||
type: array
|
||||
minItems: 1
|
||||
uniqueItems: true
|
||||
items:
|
||||
$ref: './MemberRole.yaml'
|
32
openapi/components/schemas/Organization.yaml
Normal file
32
openapi/components/schemas/Organization.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
description: Модель организации
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- createdAt
|
||||
- name
|
||||
- owner
|
||||
properties:
|
||||
id:
|
||||
$ref: './OrganizationId.yaml'
|
||||
createdAt:
|
||||
description: Дата и время создания организации
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
name:
|
||||
description: Название организации в человекочитаемом виде
|
||||
type: string
|
||||
minLength: 4
|
||||
maxLength: 100
|
||||
example: 'stolovka3'
|
||||
owner:
|
||||
allOf:
|
||||
- description: Идентификатор пользователя-владельца организации
|
||||
- $ref: './UserId.yaml'
|
||||
metadata:
|
||||
description: |
|
||||
Произвольный, специфичный для клиента API и непрозрачный для системы набор данных, ассоциированных с
|
||||
данной организацией
|
||||
type: object
|
||||
example:
|
||||
displayName: 'НИИДАР Столовая №3'
|
6
openapi/components/schemas/OrganizationId.yaml
Normal file
6
openapi/components/schemas/OrganizationId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
description: Идентификатор организации
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
example: or_af9e76uc5b47h8b154.19b8xa61dc94
|
||||
readOnly: true
|
6
openapi/components/schemas/OrganizationJoinRequest.yaml
Normal file
6
openapi/components/schemas/OrganizationJoinRequest.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
type: object
|
||||
required:
|
||||
- invitation
|
||||
properties:
|
||||
invitation:
|
||||
$ref: './InvitationAcceptToken.yaml'
|
9
openapi/components/schemas/OrganizationMembership.yaml
Normal file
9
openapi/components/schemas/OrganizationMembership.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
type: object
|
||||
required:
|
||||
- org
|
||||
- member
|
||||
properties:
|
||||
org:
|
||||
$ref: './Organization.yaml'
|
||||
member:
|
||||
$ref: './Member.yaml'
|
4
openapi/components/schemas/ResourceScopeId.yaml
Normal file
4
openapi/components/schemas/ResourceScopeId.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
description: Идентификатор ограничения для роли
|
||||
type: string
|
||||
enum:
|
||||
- Shop
|
20
openapi/components/schemas/Role.yaml
Normal file
20
openapi/components/schemas/Role.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
description: Роль в организации
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
properties:
|
||||
id:
|
||||
$ref: './RoleId.yaml'
|
||||
name:
|
||||
description: Название роли в человекочитаемом виде
|
||||
type: string
|
||||
minLength: 4
|
||||
maxLength: 100
|
||||
example: 'Shop Manager'
|
||||
scopes:
|
||||
description: Возможные области действия для роли
|
||||
type: array
|
||||
uniqueItems: true
|
||||
items:
|
||||
$ref: './ResourceScopeId.yaml'
|
7
openapi/components/schemas/RoleId.yaml
Normal file
7
openapi/components/schemas/RoleId.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
description: Идентификатор роли
|
||||
type: string
|
||||
enum:
|
||||
- Administrator
|
||||
- Accountant
|
||||
- Integrator
|
||||
- Manager
|
6
openapi/components/schemas/UserEmail.yaml
Normal file
6
openapi/components/schemas/UserEmail.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
description: Адрес электронной почты пользователя в организации
|
||||
type: string
|
||||
minLength: 4
|
||||
maxLength: 100
|
||||
format: email
|
||||
example: 'laura.palmer@niidar.su'
|
6
openapi/components/schemas/UserId.yaml
Normal file
6
openapi/components/schemas/UserId.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
description: Идентификатор пользователя
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
example: 'fjbvae05:44d0-4oz4-8745.6dql4xc6a75c'
|
||||
readOnly: true
|
14
openapi/components/security-schemes/Bearer.yaml
Normal file
14
openapi/components/security-schemes/Bearer.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
description: >
|
||||
Для аутентификации вызовов мы используем [JWT](https://jwt.io).
|
||||
Токен доступа передается в заголовке.
|
||||
|
||||
```shell
|
||||
Authorization: Bearer {JWT}
|
||||
```
|
||||
|
||||
Запросы к данному API авторизуются сессионным токеном доступа,
|
||||
который вы получаете в результате аутентификации в личном кабинете
|
||||
по адресу https://dashboard.rbk.money/.
|
50
openapi/docs/api.md
Normal file
50
openapi/docs/api.md
Normal file
@ -0,0 +1,50 @@
|
||||
RBKmoney Organizations API является интерфейсом для управления различными
|
||||
аспектами вашей организации. Все изменения состояния организации, будь то
|
||||
приглашение новых сотрудников, добавление ролей уже существующим сотрудникам
|
||||
или настройка области их ответственности осуществляются с помощью вызовов
|
||||
соответствующих методов API. Любые сторонние приложения, включая ваш личный кабинет,
|
||||
являются внешними приложениями-клиентами.
|
||||
|
||||
Мы предоставляем REST API поверх HTTP-протокола, схема которого описывается в
|
||||
соответствии со стандартом [OpenAPI 3][OAS3].
|
||||
Коды возврата описываются соответствующими HTTP-статусами. Платформа принимает и
|
||||
возвращает значения JSON в теле запросов и ответов.
|
||||
|
||||
[OAS3]: https://swagger.io/specification/
|
||||
|
||||
## Формат содержимого
|
||||
|
||||
Любой запрос к API должен выполняться в кодировке UTF-8 и с указанием
|
||||
содержимого в формате JSON.
|
||||
|
||||
```
|
||||
Content-Type: application/json; charset=utf-8
|
||||
```
|
||||
|
||||
## Запросы
|
||||
|
||||
Любой вызов методов API обязан предваряться предоставлением уникального для клиента
|
||||
платформы идентификатора запроса. Данный ID передается в соответствующем заголовке каждого
|
||||
HTTP-запроса:
|
||||
|
||||
```
|
||||
X-Request-ID: RQID-Z08G3EFE5DZ429VVO755BM19D51
|
||||
```
|
||||
|
||||
Мы требуем его, чтобы иметь возможность отследить жизненный цикл любого отдельного запроса
|
||||
в системе, когда того требуют задачи аудита или обращения в техническую поддержку.
|
||||
|
||||
### Идемпотентность
|
||||
|
||||
При совершении некоторых запросов вы можете указать _ключ идемпотентности_ – уникальный набор
|
||||
символов для обеспечения идемпотентной обработки запросов.
|
||||
|
||||
```
|
||||
X-Idempotency-Key: 881D:08BA
|
||||
```
|
||||
|
||||
Даже если платформа получит множество запросов на совершение определённой операции с одним и тем
|
||||
же значением ключа идемпотентности, эта операция будет выполнена _не более чем один_ раз.
|
||||
Таким образом, в случае кратковременных проблем с сетевой доступностью вы можете отправлять запросы
|
||||
повторно и быть уверенными в том, что, например, приглашение новому сотруднику в итоге будет
|
||||
отправлено только один раз.
|
34
openapi/docs/error-codes.md
Normal file
34
openapi/docs/error-codes.md
Normal file
@ -0,0 +1,34 @@
|
||||
## Общие ошибки
|
||||
|
||||
Ошибки возникающие при попытках совершения недопустимых операций, операций с невалидными объектами или несуществующими ресурсами. Имеют следующий вид:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "string",
|
||||
"message": "string"
|
||||
}
|
||||
```
|
||||
|
||||
В поле `message` содержится информация по произошедшей ошибке. Например:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "invalidRequest",
|
||||
"message": "Property 'name' is required."
|
||||
}
|
||||
```
|
||||
|
||||
## Ошибки обработки запросов
|
||||
|
||||
В процессе обработки запросов силами нашей платформы могут происходить различные непредвиденные ситуации. Об их появлении платформа сигнализирует по протоколу HTTP соответствующими [статусами][5xx], обозначающими ошибки сервера.
|
||||
|
||||
| Код | Описание |
|
||||
| ------- | ---------- |
|
||||
| **500** | В процессе обработки платформой запроса возникла непредвиденная ситуация. При получении подобного кода ответа мы рекомендуем обратиться в техническую поддержку. |
|
||||
| **503** | Платформа временно недоступна и не готова обслуживать данный запрос. Запрос гарантированно не выполнен, при получении подобного кода ответа попробуйте выполнить его позднее, когда доступность платформы будет восстановлена. |
|
||||
| **504** | Платформа превысила допустимое время обработки запроса, результат запроса не определён. Попробуйте отправить запрос повторно или выяснить результат выполнения исходного запроса, если повторное исполнение запроса нежелательно. |
|
||||
|
||||
[5xx]: https://tools.ietf.org/html/rfc7231#section-6.6
|
||||
|
||||
|
||||
Если вы получили ошибку, которой нет в данном описании, обратитесь в техническую поддержку.
|
58
openapi/openapi.yaml
Normal file
58
openapi/openapi.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: RBKmoney Organizations API
|
||||
description:
|
||||
$ref: './docs/api.md'
|
||||
termsOfService: 'https://rbk.money/'
|
||||
contact:
|
||||
name: RBKmoney Support Team
|
||||
email: support@rbk.money
|
||||
url: 'https://developer.rbk.money'
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
security:
|
||||
- bearer: []
|
||||
tags:
|
||||
- name: orgs
|
||||
x-displayName: Организации
|
||||
- name: members
|
||||
x-displayName: Сотрудники
|
||||
- name: invitations
|
||||
x-displayName: Приглашения
|
||||
- name: roles
|
||||
x-displayName: Роли
|
||||
- name: error-codes
|
||||
x-displayName: Коды ошибок
|
||||
description:
|
||||
$ref: './docs/error-codes.md'
|
||||
paths:
|
||||
/user/membership:
|
||||
$ref: ./paths/membership.yaml
|
||||
/user/membership/{orgId}:
|
||||
$ref: ./paths/orgMembership.yaml
|
||||
/orgs:
|
||||
$ref: ./paths/orgs.yaml
|
||||
/orgs/{orgId}:
|
||||
$ref: ./paths/org.yaml
|
||||
/orgs/{orgId}/roles:
|
||||
$ref: ./paths/roles.yaml
|
||||
/orgs/{orgId}/roles/{roleId}:
|
||||
$ref: ./paths/role.yaml
|
||||
/orgs/{orgId}/members:
|
||||
$ref: ./paths/members.yaml
|
||||
/orgs/{orgId}/members/{userId}:
|
||||
$ref: ./paths/member.yaml
|
||||
/orgs/{orgId}/members/{userId}/roles:
|
||||
$ref: ./paths/memberRoles.yaml
|
||||
/orgs/{orgId}/invitations:
|
||||
$ref: ./paths/invitations.yaml
|
||||
/orgs/{orgId}/invitations/{invitationId}:
|
||||
$ref: ./paths/invitation.yaml
|
||||
servers:
|
||||
- url: 'https://api.rbk.money/org/v1'
|
||||
components:
|
||||
securitySchemes:
|
||||
bearer:
|
||||
$ref: './components/security-schemes/Bearer.yaml'
|
71
openapi/paths/invitation.yaml
Normal file
71
openapi/paths/invitation.yaml
Normal file
@ -0,0 +1,71 @@
|
||||
get:
|
||||
summary: Получить данные приглашения
|
||||
operationId: getInvitation
|
||||
tags:
|
||||
- invitations
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/invitationId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Приглашение найдено
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Invitation.yaml'
|
||||
'404':
|
||||
description: Приглашение не найдено
|
||||
'403':
|
||||
description: Данные недоступны
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
||||
|
||||
patch:
|
||||
summary: Отозвать приглашение
|
||||
operationId: revokeInvitation
|
||||
tags:
|
||||
- invitations
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/invitationId.yaml'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- Revoked
|
||||
reason:
|
||||
description: Причина отзыва приглашения
|
||||
type: string
|
||||
maxLength: 1000
|
||||
responses:
|
||||
'204':
|
||||
description: Приглашение отозвано
|
||||
'422':
|
||||
description: Отзыв невозможен
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- invalidStatus
|
||||
message:
|
||||
description: Человекочитаемое описание ошибки
|
||||
type: string
|
||||
'404':
|
||||
description: Приглашение не найдено
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
78
openapi/paths/invitations.yaml
Normal file
78
openapi/paths/invitations.yaml
Normal file
@ -0,0 +1,78 @@
|
||||
get:
|
||||
summary: Перечислить приглашения
|
||||
operationId: listInvitations
|
||||
tags:
|
||||
- invitations
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- description: Фильтр по статусу приглашения
|
||||
name: status
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
$ref: '../components/schemas/InvitationStatusName.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Найдены приглашения
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- results
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../components/schemas/Invitation.yaml'
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
||||
|
||||
post:
|
||||
summary: Сформировать приглашение
|
||||
operationId: createInvitation
|
||||
tags:
|
||||
- invitations
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/idempotencyKey.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
requestBody:
|
||||
description: Данные нового приглашения
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Invitation.yaml'
|
||||
responses:
|
||||
'201':
|
||||
description: Приглашение сформировано
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Invitation.yaml'
|
||||
'422':
|
||||
description: Невозможно совершить операцию
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- invalidRole
|
||||
message:
|
||||
description: Человекочитаемое описание ошибки
|
||||
type: string
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
41
openapi/paths/member.yaml
Normal file
41
openapi/paths/member.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
get:
|
||||
summary: Получить данные сотрудника
|
||||
operationId: getOrgMember
|
||||
tags:
|
||||
- members
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/userId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Данные найдены
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Member.yaml'
|
||||
'403':
|
||||
description: Данные недоступны
|
||||
'404':
|
||||
description: Сотрудник не найден
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
||||
|
||||
delete:
|
||||
summary: Исключить сотрудника из организации
|
||||
operationId: expelOrgMember
|
||||
tags:
|
||||
- members
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/userId.yaml'
|
||||
responses:
|
||||
'204':
|
||||
description: Сотрудник более не состоит в организации
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
79
openapi/paths/memberRoles.yaml
Normal file
79
openapi/paths/memberRoles.yaml
Normal file
@ -0,0 +1,79 @@
|
||||
put:
|
||||
summary: Назначить сотруднику роль
|
||||
operationId: assignMemberRole
|
||||
tags:
|
||||
- members
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/userId.yaml'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/MemberRole.yaml'
|
||||
responses:
|
||||
# TODO
|
||||
# Нужно ли возвращать получившийся набор ролей?
|
||||
'204':
|
||||
description: Роль назначена
|
||||
'422':
|
||||
# TODO
|
||||
# Может упростить пока и не энфорсить это требование на API?
|
||||
description: Невозможно совершить операцию
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- onlyRoleLeft
|
||||
message:
|
||||
description: Человекочитаемое описание ошибки
|
||||
type: string
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'404':
|
||||
description: Сотрудник не найден
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
||||
|
||||
delete:
|
||||
summary: Снять роль с сотрудника
|
||||
operationId: removeMemberRole
|
||||
tags:
|
||||
- members
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/userId.yaml'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/MemberRole.yaml'
|
||||
responses:
|
||||
'204':
|
||||
description: Сотрудник более не обладает указанной ролью
|
||||
'422':
|
||||
description: Невозможно совершить операцию
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- invalidRole
|
||||
message:
|
||||
description: Человекочитаемое описание ошибки
|
||||
type: string
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'404':
|
||||
description: Сотрудник не найден
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
28
openapi/paths/members.yaml
Normal file
28
openapi/paths/members.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
get:
|
||||
summary: Перечислить сотрудников организации
|
||||
operationId: listOrgMembers
|
||||
tags:
|
||||
- members
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Найдены сотрудники
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- results
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../components/schemas/Member.yaml'
|
||||
'403':
|
||||
description: Данные недоступны
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
69
openapi/paths/membership.yaml
Normal file
69
openapi/paths/membership.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
get:
|
||||
summary: Перечислить доступные организации
|
||||
operationId: listOrgMembership
|
||||
tags:
|
||||
- orgs
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Найдены доступные организации
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- results
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../components/schemas/Organization.yaml'
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
||||
|
||||
post:
|
||||
summary: Вступить в организацию по приглашению
|
||||
operationId: joinOrg
|
||||
tags:
|
||||
- orgs
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/OrganizationJoinRequest.yaml'
|
||||
responses:
|
||||
'201':
|
||||
description: Пользователь вступил в организацию
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/OrganizationMembership.yaml'
|
||||
headers:
|
||||
location:
|
||||
description: URL членства в организации
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uri
|
||||
example: 'https://api.rbk.money/org/v1/user/membership/or_af9e76uc5b47h8b154.19b8xa61dc94'
|
||||
'422':
|
||||
description: Невозможно совершить операцию
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- invitationExpired
|
||||
message:
|
||||
description: Человекочитаемое описание ошибки
|
||||
type: string
|
||||
'403':
|
||||
description: Вступление невозможно
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
19
openapi/paths/org.yaml
Normal file
19
openapi/paths/org.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
get:
|
||||
summary: Получить данные организации
|
||||
operationId: getOrg
|
||||
tags:
|
||||
- orgs
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Организация найдена
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Organization.yaml'
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
37
openapi/paths/orgMembership.yaml
Normal file
37
openapi/paths/orgMembership.yaml
Normal file
@ -0,0 +1,37 @@
|
||||
get:
|
||||
summary: Посмотреть своё членство в организации
|
||||
operationId: inquireOrgMembership
|
||||
tags:
|
||||
- orgs
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Получены данные членства
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/OrganizationMembership.yaml'
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
||||
|
||||
delete:
|
||||
summary: Отменить своё членство в организации
|
||||
operationId: cancelOrgMembership
|
||||
tags:
|
||||
- orgs
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
responses:
|
||||
'204':
|
||||
description: Членство отменено
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'403':
|
||||
description: Операция недоступна
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
24
openapi/paths/orgs.yaml
Normal file
24
openapi/paths/orgs.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
post:
|
||||
summary: Создать новую организацию
|
||||
operationId: createOrg
|
||||
tags:
|
||||
- orgs
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/idempotencyKey.yaml'
|
||||
requestBody:
|
||||
description: Данные организации
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Organization.yaml'
|
||||
responses:
|
||||
'201':
|
||||
description: Организация создана
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Organization.yaml'
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
22
openapi/paths/role.yaml
Normal file
22
openapi/paths/role.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
get:
|
||||
summary: Получить данные о роли
|
||||
operationId: getOrgRole
|
||||
tags:
|
||||
- roles
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
- $ref: '../components/parameters/roleId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Найдены роли
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/Role.yaml'
|
||||
'403':
|
||||
description: Данные недоступны
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
28
openapi/paths/roles.yaml
Normal file
28
openapi/paths/roles.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
get:
|
||||
summary: Перечислить доступные роли в организации
|
||||
operationId: listOrgRoles
|
||||
tags:
|
||||
- roles
|
||||
parameters:
|
||||
- $ref: '../components/parameters/requestId.yaml'
|
||||
- $ref: '../components/parameters/orgId.yaml'
|
||||
responses:
|
||||
'200':
|
||||
description: Найдены роли
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- results
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../components/schemas/Role.yaml'
|
||||
'403':
|
||||
description: Данные недоступны
|
||||
'404':
|
||||
description: Организация не найдена
|
||||
'400':
|
||||
$ref: '../components/responses/BadRequest.yaml'
|
3102
package-lock.json
generated
Normal file
3102
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "rbkmoney-organizations-api",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@redocly/openapi-cli": "^0.12.7",
|
||||
"redoc-cli": "^0.9.8",
|
||||
"json-merge-patch": "^0.2.3",
|
||||
"json-pointer": "^0.6.0",
|
||||
"shelljs": "^0.7.0",
|
||||
"swagger-ui-dist": "^3.18.3"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "openapi preview-docs",
|
||||
"build": "node ./build.js",
|
||||
"validate": "openapi validate",
|
||||
"openapi": "openapi",
|
||||
"redoc": "redoc-cli"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user