HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
JustinSun

НОВОСТИ [Перевод] От 48k до 10 строк кода — история GitHub JavaScript SDK

BDFpromo
Оффлайн

BDFpromo

.
.
Регистрация
23.09.18
Сообщения
12.347
Реакции
176
Репутация
0
9lzi3kc9ivgzrrzghpyddw_rdfe.png



@octokit/rest изначально не является оригинальной разработкой GitHub, и представляет собой адаптацию — самого популярного пакета 2017 года от пользователя . В этом посте будем говорить про — теперь официальный JavaScript SDK для .


Грегор (автор статьи) является разработчиком JavaScript Octokit. Он бывалый open source разработчик, с особой тягой к автоматизации задач и снижению порога вхождения для контрибьюторов всех видов и профессий. Помимо Octokit, Грегор работает над , и . В свободное время он заботится о своих тройняшках Нико, Аде и Киане. Больше материалов Грегора можно найти на и .​

legacy



Позже переименованный в @octokit/rest, пакет github был одним из старейших проектов в экосистеме Node. сделан в июне 2010 года. Это были времена , когда package.json еще не существовал, а реестр npm был всё ещё в разработке.


В 2017 году GitHub наняли меня, чтобы переработать пакет github в официальный GitHub API JavaScript SDK для браузеров и Node.js. Здесь можно найти в сентябре 2017 года. На тот момент в проекте было около 16 тысяч строк кода, разбитых на три JavaScript файла, один огромный JSON и два файла для определений типов TypeScript/Flow.


➜ rest.js git:(50720c8) wc -l lib/*
120 lib/error.js
3246 lib/index.d.ts
905 lib/index.js
3232 lib/index.js.flow
17 lib/promise.js
7995 lib/routes.json
143 lib/util.js
15658 total

Разработка



Первой основной целью проекта была поддерживаемость кода. Тогда ключевым компонентом библиотеки был огромный файл на почти 8 тысяч строк, который определял все конечные точки REST API GitHub. Его поддержка осуществлялась вручную, а создание/изменение роутов было следствием .


Учитывая этот факт, я написал скрипт ( ) для автоматического анализа документации REST API GitHub и вывода результата в JSON. Это решило проблему поддержки routes.json. Если скрипт обнаруживал изменения, @octokit/rest получал PR с обновлениями файла routes.json, и после мерджа происходил автоматический релиз. Благодаря такой автоматизации, файл routes.json теперь гарантированно покрывал все конечные точки REST API GitHub и состоял из . Сопутствующие определения типов TypeScript увеличились до более чем .

Архитектура



Как только вопрос полноте и поддерживаемости API был решен, я сосредоточился на другой цели проекта: .

Прим. переводчика: В оригинальной статье используется термин "decomposability", который дословно можно перевести как "разложимость", что не совсем удачно передает смысл. Здесь имеется ввиду "разделение на части", что можно объяснить как "разделение на модули" или "модульность".​

JavaScript Octokit предназначен для всех сред выполнения JavaScript, некоторые из которых имеют строгие ограничения. Например, размер пакета является критическим показателем при использовании в браузере. Поэтому, вместо единой монолитной библиотеки, которая содержит полный REST API, стратегии аутентификации и рекомендованный вспомогательный функционал (например "пагинация"), важно предоставить пользователям доступ к более низкому уровню. Таким образом, будет возможен компромисс между размером пакета и предоставляемым функционалом.


Вот архитектуры, которую я разработал в январе 2018 года:


gvb-0clno44zhg4ymrwxnfsisti.png



Результат внутреннего рефакторинга до новой архитектуры:
Обратите внимание, что этот пример был упрощен для удобства чтения


➜ rest.js git:(f7c9f86) wc -l index.* lib/**/*.{js,json}
31 index.js
3474 index.d.ts
3441 index.js.flow

101 lib/endpoint/ # 4 files
162 lib/request/ # 3 files

83 lib/plugins/authentication/ # 3 files
130 lib/plugins/endpoint-methods/ # 4 files
130 lib/plugins/pagination/ # 11 files

58 lib/parse-client-options.js
10628 lib/routes.json

18238 total


В течение следующих шести месяцев я реорганизовал код и начал извлекать некоторые из модулей:

  • : превращает параметры конечной точки API REST в общие параметры http-запроса
  • : отправляет параметризованные запросы в API GitHub с разумными настройками по умолчанию в браузерах и Node
  • : API использующееся для подключения к жизненному циклу запроса


В ноябре 2018 года, после использования плагинов внутри проекта в течение примерно шести месяцев, была анонсирована включающая API для плагинов. Большая часть библиотеки была переведена на систему , для их дальнейшего извлечения.


Новая архитектура внутреннего кода теперь выглядела так:
Обратите внимание, что этот пример был упрощен для удобства чтения


➜ rest.js git:(01763bf) wc -l index.* plugins/**/*.{js,json} lib/**/*.js
14 index.js
26714 index.d.ts

110 lib/ # 6 files

86 plugins/authentication/ # 3 files
77 plugins/pagination/ # 3 files
39 plugins/register-endpoints/ # 3 files
108 plugins/validate/ # 2 files
10275 plugins/rest-api-endpoints/routes.json
37423 total


Позже я создал — новую базовую библиотеку Octokit JavaScript, на которой будут основываться @octokit/rest и остальные библиотеки группы Octokit. Большая часть его логики была извлечена из @octokit/rest за исключением устаревших функций. Я не стал использовать его сразу в @octokit/rest, чтобы избежать критических изменений.


Поскольку @octokit/core был свободен от любого legacy и проблем с обратной совместимостью, я продолжил эксперименты с разбивкой средств аутентификации. Результатом стали отдельные пакеты для каждой стратегии аутентификации — все они перечислены в . Если вы хотите узнать больше о стратегиях аутентификации GitHub, то предлагаю ознакомиться с моей .


@octokit/core и отдельные библиотеки аутентификации заменяют весь код из lib/* и plugins/authentication/*. Осталось только три плагина, которые я извлек в дальнейшем:



Плагин validate устарел благодаря типизации от TypeScript во время компиляции и больше не было необходимости проверять параметры запроса на клиенте. Это значительно уменьшило количество кода и зависимостей. Например, вот текущее определение для метода octokit.checks.create():


{
checks: {
create: {
headers: { accept: "application/vnd.github.antiope-preview+json" },
method: "POST",
params: {
actions: { type: "object[]" },
"actions[].description": { required: true, type: "string" },
"actions[].identifier": { required: true, type: "string" },
"actions[].label": { required: true, type: "string" },
completed_at: { type: "string" },
conclusion: {
enum: [
"success",
"failure",
"neutral",
"cancelled",
"timed_out",
"action_required"
],
type: "string"
},
details_url: { type: "string" },
external_id: { type: "string" },
head_sha: { required: true, type: "string" },
name: { required: true, type: "string" },
output: { type: "object" },
"output.annotations": { type: "object[]" },
"output.annotations[].annotation_level": {
enum: ["notice", "warning", "failure"],
required: true,
type: "string"
},
"output.annotations[].end_column": { type: "integer" },
"output.annotations[].end_line": { required: true, type: "integer" },
"output.annotations[].message": { required: true, type: "string" },
"output.annotations[].path": { required: true, type: "string" },
"output.annotations[].raw_details": { type: "string" },
"output.annotations[].start_column": { type: "integer" },
"output.annotations[].start_line": { required: true, type: "integer" },
"output.annotations[].title": { type: "string" },
"output.images": { type: "object[]" },
"output.images[].alt": { required: true, type: "string" },
"output.images[].caption": { type: "string" },
"output.images[].image_url": { required: true, type: "string" },
"output.summary": { required: true, type: "string" },
"output.text": { type: "string" },
"output.title": { required: true, type: "string" },
owner: { required: true, type: "string" },
repo: { required: true, type: "string" },
started_at: { type: "string" },
status: { enum: ["queued", "in_progress", "completed"], type: "string" }
},
url: "/repos/:eek:wner/:repo/check-runs"
}
}
}


Начиная с v17, определение того же метода выглядит следующим образом :


{
checks: {
create: [
"POST /repos/{owner}/{repo}/check-runs",
{ mediaType: { previews: ["antiope"] } },
];
}
}


В конце, ранее извлеченный код был собран в обещанные 10 строк:


import { Octokit as Core } from "@octokit/core";
import { requestLog } from "@octokit/plugin-request-log";
import { paginateRest } from "@octokit/plugin-paginate-rest";
import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods";

import { VERSION } from "./version";

export const Octokit = Core.plugin([
requestLog,
paginateRest,
restEndpointMethods,
]).defaults({ userAgent: `octokit-rest.js/${VERSION}` });

Тесты



Каждая строка кода была изменена между версиями v16 и v17, поэтому единственный способ убедиться в отсутствии новых ошибок — провести полное тестирование.


На момент создания модуля в 2017 году у нас не было никаких тестов, но были . Первым делом я превратил их в интеграционные тесты, а поскольку JavaScript Octokit SDK задумывался как основа SDK для всех популярных языков, я создал — независимый от языка, автоматически обновляемый набор макетов http для общих случаев использования.


Для оставшейся логики, специфичной для @octokit/rest, были написаны интеграционные тесты для 100% покрытия. На сегодняшний момент, если значение опустится ниже 100% тесты упадут.

Прим. переводчика:

Работая над переходом на v17 с 10 строками кода, я продолжал запускать тесты из v16, за исключением тестов для устаревшего API. В то же время, слишком большое количество тестов не слишком хорошо, поэтому после успешной проверки v17, тесты не относящиеся к @octokit/rest были удалены. Некоторые из них были перенесены в плагины, @octokit/core или @octokit/request. На данный момент осталось несколько smoke-тестов и сценариев с использованием @octokit/fixtures.

Будущее



@octokit/rest начинался как пакет для REST API GitHub, но с 17 версии, это будет полноценная библиотека JavaScript, включающая вспомогательные функции для API @octokit/rest, с пагинацией, троттлингом и повторными запросами. Также будут поддерживаться все существующие и будущие стратегии аутентификации и GraphQL, так как он является частью @octokit/core.


В конце, я хотел бы сказать спасибо , и , которые создали и поддерживали модуль github до того, как он превратился в @octokit/rest.
 
Сверху Снизу