{"action":"created","comment":{"url":"https://api.github.com/repos/devSchacht/translations/pulls/comments/167772956","pull_request_review_id":96037934,"id":167772956,"diff_hunk":"@@ -0,0 +1,645 @@\n+# Создание MEVN-приложения (Часть 2/2)\n+\n+![logo](./images/logo.png)\n+\n+*Перевод статьи [Aneeta Sharma](https://twitter.com/anaida07): [Build full stack web apps with MEVN Stack [Part 2/2]](https://medium.com/devnetwork/mevn-stack-application-part-2-2-9ebcf8a22753). Опубликовано с разрешения автора.*\n+\n+***\n+\n+Эта статья является продолжением [предыдущей части руководства](https://medium.com/@gearmobile/%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-mevn-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D1%87%D0%B0%D1%81%D1%82%D1%8C-1-2-9ad714260037), посвященного цели создания базового функционала приложения, разрабатываемого на стеке технологий MongoDB, Express.js, Vue.js and Node.js.\n+\n+В этой части будет уделено внимание вопросу операций CRUD (Create, Read, Update, Delete), выполняемых при помощи Express.js и MongoDB (для работы с MongoDB мы будем использовать [Mongoose](http://mongoosejs.com/)).\n+\n+С исходным кодом готового проекта можно ознакомиться в репозитории на Gitlab - [MEVN Application](https://gitlab.com/learning-stacks/learning-express.js/tree/master/mevn-application).\n+\n+## Что было сделано в предыдущей части\n+\n+* был создан базовый каркас приложения\n+* было создано соединение между frontend(Vue.js) и backend(Express.js) частями приложения\n+\n+## Что будет сделано в этой части\n+\n+* реализация CRUD-операций при помощи Mongoose\n+\n+В предыдущей части мы остановились на следующем месте создания приложения. В клиентской части при переходе по адресу `http://localhost:8080/posts` мы ожидаем отображения в окне браузера списка всех записей (posts), которые получаем с сервера при помощи функции `fetchPosts` модуля `PostService.js`.\n+\n+Давайте снова запустим клиентскую часть командой `yarn run dev` и серверную часть командой `yarn start`.\n+\n+Процесс установки и запуска MongoDB опустим, так как это выходит за рамки этой и так довольно объемной статьи; подразумевается, что читатели данного руководства уже обладают достаточным уровнем знаний в этой области.\n+\n+Теперь если в браузере мы перейдем по адресу `http://localhost:8080/posts`, то увидим такую страницу (что и ожидалось):\n+\n+![vuew posts](./images/vue-02-01.png)\n+\n+Создадим подключение к MongoDB при помощи Mongoose. Для этого сначала установим пакет `mongoose` как зависимость проекта:\n+\n+```js\n+$ yarn add mongoose\n+```\n+\n+И подключим установленный `mongoose` в главном файле сервера:\n+\n+```js\n+const mongoose = require('mongoose')\n+```\n+\n+Помимо этого сделаем замену промиса библиотеки Mongoose нативным промисом Node.js:\n+\n+```js\n+mongoose.Promise = global.Promise\n+```\n+\n+Осталось только подключить Mongoose к MongoDB и связать это действие с запуском сервера `express` для большей логичности:\n+\n+```javascript\n+mongoose.connect(config.dbURL, config.dbOptions)\n+mongoose.connection\n+  .once('open', () => {\n+    console.log(`Mongoose - successful connection ...`)\n+    app.listen(process.env.PORT || config.port, () => console.log(`Server start on port ${config.port} ...`))\n+  })\n+  .on('error', error => console.warn(error))\n+```\n+\n+Что происходит в этой части кода? В строке `mongoose.connect` мы подключаем Mongoose к MongoDB. Адрес подключения и опции подключения указаны в файле `config/config.js`:\n+\n+```javascript\n+module.exports = {\n+  port: 8081,\n+  dbURL: 'mongodb://localhost/articles',\n+  dbOptions: { useMongoClient: true }\n+}\n+```\n+\n+Затем в строке `mongoose.connection` мы становимся на прослушку события подключения Mongoose к MongoDB. Если соединение произошло успешно (событие `open`), то при помощи метода `once` сообщаем в консоли о данном факте; следующим действием запускаем сервер `express` - `app.listen`.\n+\n+Если же при соединении произошла ошибка `error`, то при помощи метода `on` сообщаем об этом в консоли.\n+\n+**Примечание переводчика**: *для полноты картины можно было бы еще обработать событие `disconnected`, если вдруг Mongoose \"отвалится\" от MongoDB.*\n+\n+Можно задать вопрос - зачем такие сложности в данном примере кода? На самом деле здесь все правильно и логично, так как мы запускаем сервер `express` только тогда, когда произошло успешное подключение к базе данных MongoDB.\n+\n+## Mongoose - создание Schema\n+\n+Прежде чем продолжать работать с MongoDB, мы должны отступить слегка в сторону Mongoose.\n+\n+Основная цель создания и использования Mongoose - это типизация и верификация данных, записываемых в MongoDB. Другими словами, в Mongoose есть стандартизированный тип данных, создаваемый конкретно для одной из баз данных в MongoDB.\n+\n+Такой тип данных в Mongoose называется Schema. Для нашей базы данных articles мы создадим Schema под названием PostSchema. Для этого создадим поддиректорию `src/models` и в ней файл `post-model.js` с таким содержанием:\n+\n+```javascript\n+const mongoose = require('mongoose')\n+const Schema = mongoose.Schema\n+\n+const PostSchema = new Schema({\n+  title: {\n+    type: String,\n+    unique: true\n+  },\n+  description: {\n+    type: String\n+  }\n+})\n+\n+const PostModel = mongoose.model('posts', PostSchema)\n+\n+module.exports = PostModel\n+```\n+\n+Что происходит в данном файле? В нем мы подключаем библиотеку Mongoose для того, чтобы воспользоваться ее методом Schema. При помощи метода Schema создаем свой пользовательский тип данных PostSchema, в котором говорим, что все данные, которые будут записываться в базу данных articles, должны быть объектами и иметь текстовое поле `title` и текстовое поле `description`.\n+\n+Затем при помощи PostSchema мы создаем пользовательскую модель PostModel при помощи метода `model` библиотеки Mongoose. Библиотека Mongoose \"прикручивает\" к модели PostModel коллекцию методов, например метод `save()`, `remove()`, `find()` и многие другие.\n+\n+Эту модель мы экспортируем и при помощи этой модели (*и ее методов*) будем создавать и сохранять данные в articles.\n+\n+## Express - создание маршрутов\n+\n+Прежде чем продолжать работу с моделями Mongoose, внесем в главный файл `src/index.js` изменение - добавим поддержку router. Это делается для того, чтобы поддержать чистоту и читаемость кода.\n+\n+Создадим поддиректорию `src/routes` и в нем файл `posts.js`, в котором будут содержаться все маршруты, связанные с endpoint  - `/posts`. В этом файле также подключим модель PostModel:\n+\n+```javascript\n+const Post = require('../models/post-model')\n+```\n+\n+В итоге заголовок файла `src/routes.posts.js` будет выглядеть таким образом:\n+\n+```javascript\n+const express = require('express')\n+const router = express.Router()\n+const Post = require('../models/post-model')\n+```\n+\n+В главном файле `src/index.js` подключим файл с маршрутами:\n+\n+```javascript\n+app.use(require('./routes/posts'))\n+```\n+\n+Полный вариант индексного файла будет выглядеть таким образом:\n+\n+```javascript\n+const express = require('express')\n+const bodyParser = require('body-parser')\n+const cors = require('cors')\n+const morgan = require('morgan')\n+const mongoose = require('mongoose')\n+const config = require('./config/config')\n+mongoose.Promise = global.Promise\n+\n+const app = express()\n+app.use(morgan('combined'))\n+app.use(bodyParser.json())\n+app.use(cors())\n+\n+app.use(require('./routes/posts'))\n+\n+mongoose.connect(config.dbURL, config.dbOptions)\n+mongoose.connection\n+  .once('open', () => {\n+    console.log(`Mongoose - successful connection ...`)\n+    app.listen(process.env.PORT || config.port, () => console.log(`Server start on port ${config.port} ...`))\n+  })\n+  .on('error', error => console.warn(error))\n+```\n+\n+## Операция CREATE\n+\n+В файле `src/routes/posts.js` создадим обработку операции CREATE - создания и записи в базу данных articles:\n+\n+```javascript\n+// => create post\n+router.post('/posts', (req, res) => {\n+  const post = new Post({\n+    title: req.body.title,\n+    description: req.body.description\n+  })\n+  post.save((err, data) => {\n+    if (err) {\n+      console.log(err)\n+    } else {\n+      res.send({\n+        success: true,\n+        message: `Post with ID_${data._id} saved successfully!`\n+      })\n+    }\n+  })\n+})\n+```\n+\n+Что происходит в этом участке кода? Здесь идет обработка POST-запроса на endpoint `/posts`. Функция обратного вызова создает новый объект `post` при помощи модели Post. В качестве значения полей `title` и `description` будут использоваться приходящие со стороны клиента значения поля `body` объекта `request`.\n+\n+Экземпляр `post` модели Post наследует у него методы для записи и редактирования в базу данных. В данном случае мы воспользуемся методом `save` для записи объекта `post` в базу данных MongoDB. Если при записи в MongoDB произошла ошибка, то выведем ее в консоль. Иначе скажем `express`, чтобы он \"отрапортовал\" нам об успешном выполнении операции при помощи метода `send`.\n+\n+Конечно же нужно протестировать обработку данного POST-запроса. Для этого я воспользуюсь очень удобным инструментом [Postman](https://www.getpostman.com/):\n+\n+![postman - post](./images/vue-02-02.gif)\n+\n+Как видим, сервер `express` успешно принимает POST-запрос и при помощи Mongoose делает запись в базу данных MongoDB.\n+\n+## Операция READ\n+\n+Операция чтения всех данных из базы данных articles выполняется следующим участком кода:\n+\n+```javascript\n+// => read all posts\n+router.get('/posts', (req, res) => {\n+  Post.find({}, 'title description', (err, posts) => {\n+    if (err) {\n+      res.sendStatus(500)\n+    } else {\n+      res.send({ posts: posts })\n+    }\n+  }).sort({ _id: -1 })\n+})\n+```\n+\n+Здесь при GET-запросе на endpoint `/posts` мы воспользуемся моделью Post и ее методом `find`, который найдет все записи в базе данных articles и вернет их методом `send`.\n+\n+Но перед этим будет выполнена сортировка найденных данных методом `sort`. Если вдруг произошла ошибка чтения базы данных, `express` сообщит об этом, отправив код ошибки 500 (\"Ошибка сервера\") методом `sendStatus`.\n+\n+Давайте получим с помощью [Postman](https://www.getpostman.com/) все имеющиеся в базе данных articles записи:\n+\n+![postman get](./images/vue-02-03.gif)\n+\n+Немного же их пока!\n+\n+## Создание и отправка записей из клиента\n+\n+Настало время настроить возможность создания и отправки данных из клиента (client). Для этого первоначально создадим новый компонент (страницу) `src/components/pages/NewPostPage.vue` и пропишем для него маршрут в `src/routes/index.js`:\n+\n+```javascript\n+import Start from '@/components/pages/StartPage'\n+import Posts from '@/components/pages/PostsPage'\n+import NewPost from '@/components/pages/NewPostPage'\n+\n+const routes = [\n+  {\n+    path: '/',\n+    name: 'Start',\n+    component: Start\n+  },\n+  {\n+    path: '/posts',\n+    name: 'Posts',\n+    component: Posts\n+  },\n+  {\n+    path: '/posts/new',\n+    name: 'NewPost',\n+    component: NewPost\n+  }\n+]\n+\n+export default routes\n+```\n+\n+Затем наполним содержимым сам компонент `NewPostPage.vue`:\n+\n+```javascript\n+<template lang=\"pug\">\n+  .container\n+    .row\n+      .col-xs-12\n+        h1\n+          | Add New Post\n+        form\n+          .form-group\n+            input.form-control( type=\"text\", name=\"title\", id=\"title\", placeholder=\"Title\", v-model.trim=\"post.title\" )\n+          .form-group\n+            textarea.form-control( type=\"text\", rows=\"5\", name=\"description\", id=\"description\", placeholder=\"Description\", v-model.trim=\"post.description\" )\n+          .form-group\n+            button.btn.btn-block.btn-primary( type=\"button\", name=\"addPost\", id=\"addPost\", @click=\"addPost()\" )\n+              | add new post\n+          section\n+            button.btn.btn-success.btn-block( type=\"button\", @click=\"goBack()\" )\n+              | go to posts page\n+</template>\n+\n+<script>\n+  import PostsService from '@/services/PostsService'\n+  export default {\n+    name: 'NewPostPage',\n+    data () {\n+      return {\n+        post: {\n+          title: '',\n+          description: ''\n+        }\n+      }\n+    },\n+    methods: {\n+      async addPost () {\n+        if (this.post.title !== '' && this.post.description !== '') {\n+          await PostsService.addNewPost({\n+            title: this.post.title,\n+            description: this.post.description\n+          })\n+          this.$router.push({ name: 'Posts' })\n+        } else {\n+          alert('Empty fields!')\n+        }\n+      },\n+      goBack () {\n+        this.$router.push({ name: 'Posts' })\n+      }\n+    }\n+  }\n+</script>\n+```\n+\n+Что мы видим в этом компоненте? Секция `template` маленькая и простая - это всего лишь форма для создания и отправки данных. Данные отправляются по нажатию на кнопку `button`.\n+\n+**Примечание переводчика**: *здесь было бы правильнее воспользоваться обработкой события `submit` формы; для этого переместить метод `addPost` на `form( @submit.prevent=\"addPost\" )` и поменять тип кнопки на `\"submit\"` ).*\n+\n+Отправка данных из компонента выполняется методом `addPost`, который через async-await делает следующее. Если значения полей формы не пустые, то вызывает на исполнение метод `addNewPost` модуля `PostsService` (мы создадим этот метод немного позже).\n+\n+Аргументом метода `addNewPost` передаем объект, который конструируем на основе значения полей формы. Как только метод `addNewPost` успешно выполнился, мы при помощи `router` возвращаемся на страницу со списком всех записей. Если вдруг произошла ошибка, то \"кричим\" об этом.\n+\n+Метод `goBack` также возвращает нас назад на страницу со списком всех записей, но только при нажатии на кнопку `button`.\n+\n+Нам осталось определить метод `addNewPost`, который и будет отправлять данные на сервер `express`. Для этого в файле `src/services/PostsService.js` добавим такое содержание:\n+\n+```javascript\n+import api from '@/services/api'\n+\n+export default {\n+  fetchPosts () {\n+    return api().get('posts')\n+  },\n+  addNewPost (params) {\n+    return api().post('posts', params)\n+  }\n+}\n+```\n+\n+Как видим, метод `addNewPost` обрабатывает POST-запрос при помощи `axios`, которому в качестве аргументов передает путь и данные `params`, которые тот отправит `express`'у.\n+\n+Все готово для создания и отправки данных. Откроем браузер по адресу `http://localhost:8080/posts/new` - должна отобразиться форма, в которой введем данные и отправим на сервер. Затем проверим, что сервер получил наши данные и записал их в базу данных. Вернемся на страницу со список всех записей - `http://localhost:8080/posts`:\n+\n+![read data](./images/vue-02-04.gif)\n+\n+Операции CREATE и READ успешно нами созданы и протестированы. Осталось создать две оставшиеся - UPDATE и DELETE.\n+\n+## Операция UPDATE\n+\n+На стороне сервера в файле `src/routes/posts.js` добавим два метода - для получения единичной записи по ID и для обновления единичной записи по ID:\n+\n+```javascript\n+// => read single post\n+router.get('/posts/:id', (req, res) => {\n+  Post.findById(req.params.id, 'title description', (err, post) => {\n+    if (err) {\n+      res.sendStatus(500)\n+    } else {\n+      res.send(post)\n+    }\n+  })\n+})\n+```\n+\n+```javascript\n+// => update single post\n+router.put('/posts/:id', (req, res) => {\n+  Post.findById(req.params.id, 'title description', (err, post) => {\n+    if (err) {\n+      console.log(err)\n+    } else {\n+      if (req.body.title) {\n+        post.title = req.body.title\n+      }\n+      if (req.body.description) {\n+        post.description = req.body.description\n+      }\n+      post.save(err => {\n+        if (err) {\n+          res.sendStatus(500)\n+        } else {\n+          res.sendStatus(200)\n+        }\n+      })\n+    }\n+  })\n+})\n+```\n+\n+В случае с получением единичной записи все просто. Мы обрабатываем GET-запрос на endpoint `/posts/:id`. Воспользуемся моделью Post и методом `findById` библиотеки Mongoose.\n+\n+Как видно из самого названия метода, он ищет в базе данных записи с указанным ID. В нашем случае ID приходит со стороны клиента как значение `id` объекта `request`. Далее мы \"говорим\" Mongoose, чтобы он вернул нам только поля `title` и `description` найденного объекта. Если все плохо, то `express` сообщает нам статус 500; если все хорошо - возвращает найденную запись.\n+\n+В случае с обновлением записи в базе данных все происходит почти также. По PUT-запросу на endpoint `/posts/:id` ищется запись с указанным ID. Если запись найдена, то выполняем две проверки. Если с клиента пришло значение поля `title`, то записываем его как значение одноименного поля `title` объекта `post`.\n+\n+Если с клиента пришло значение поля `description`, то записываем его как значение одноименного поля `description` объекта `post`. Тем самым мы обновляем найденный объект полученными с клиента данными.\n+\n+Осталось только записать обновленный `post` снова в базу данных. Express рапортует об успешности или неуспешности записи в базу данных.\n+\n+## Обновление записи из клиента\n+\n+В клиентской части приложения добавляем еще один компонент (страницу) - `EditPostPage.vue`. В это компоненте мы будет получать из базы данных единичную запись по ее ID; редактировать поля `title` и `description` этой записи; и снова отправлять эту запись на сервер для записи в базу данных, тем самым обновляя ее.","path":"articles/aneeta-sharma-mevn-stack-application-part-2/readme.md","position":398,"original_position":398,"commit_id":"b61f3de54550f1065b888a804026d9820309d497","original_commit_id":"b61f3de54550f1065b888a804026d9820309d497","user":{"login":"gearmobile","id":8580397,"avatar_url":"https://avatars3.githubusercontent.com/u/8580397?v=4","gravatar_id":"","url":"https://api.github.com/users/gearmobile","html_url":"https://github.com/gearmobile","followers_url":"https://api.github.com/users/gearmobile/followers","following_url":"https://api.github.com/users/gearmobile/following{/other_user}","gists_url":"https://api.github.com/users/gearmobile/gists{/gist_id}","starred_url":"https://api.github.com/users/gearmobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gearmobile/subscriptions","organizations_url":"https://api.github.com/users/gearmobile/orgs","repos_url":"https://api.github.com/users/gearmobile/repos","events_url":"https://api.github.com/users/gearmobile/events{/privacy}","received_events_url":"https://api.github.com/users/gearmobile/received_events","type":"User","site_admin":false},"body":"done","created_at":"2018-02-13T06:44:32Z","updated_at":"2018-02-13T06:44:32Z","html_url":"https://github.com/devSchacht/translations/pull/132#discussion_r167772956","pull_request_url":"https://api.github.com/repos/devSchacht/translations/pulls/132","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/devSchacht/translations/pulls/comments/167772956"},"html":{"href":"https://github.com/devSchacht/translations/pull/132#discussion_r167772956"},"pull_request":{"href":"https://api.github.com/repos/devSchacht/translations/pulls/132"}},"in_reply_to_id":167674703},"pull_request":{"url":"https://api.github.com/repos/devSchacht/translations/pulls/132","id":168550899,"html_url":"https://github.com/devSchacht/translations/pull/132","diff_url":"https://github.com/devSchacht/translations/pull/132.diff","patch_url":"https://github.com/devSchacht/translations/pull/132.patch","issue_url":"https://api.github.com/repos/devSchacht/translations/issues/132","number":132,"state":"open","locked":false,"title":"aneeta-sharma-mevn-stack-application-part-2 - init","user":{"login":"gearmobile","id":8580397,"avatar_url":"https://avatars3.githubusercontent.com/u/8580397?v=4","gravatar_id":"","url":"https://api.github.com/users/gearmobile","html_url":"https://github.com/gearmobile","followers_url":"https://api.github.com/users/gearmobile/followers","following_url":"https://api.github.com/users/gearmobile/following{/other_user}","gists_url":"https://api.github.com/users/gearmobile/gists{/gist_id}","starred_url":"https://api.github.com/users/gearmobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gearmobile/subscriptions","organizations_url":"https://api.github.com/users/gearmobile/orgs","repos_url":"https://api.github.com/users/gearmobile/repos","events_url":"https://api.github.com/users/gearmobile/events{/privacy}","received_events_url":"https://api.github.com/users/gearmobile/received_events","type":"User","site_admin":false},"body":"init part 2","created_at":"2018-02-12T11:09:19Z","updated_at":"2018-02-13T06:44:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2af973a0173378662beb9018d689c46e87215895","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/devSchacht/translations/pulls/132/commits","review_comments_url":"https://api.github.com/repos/devSchacht/translations/pulls/132/comments","review_comment_url":"https://api.github.com/repos/devSchacht/translations/pulls/comments{/number}","comments_url":"https://api.github.com/repos/devSchacht/translations/issues/132/comments","statuses_url":"https://api.github.com/repos/devSchacht/translations/statuses/b61f3de54550f1065b888a804026d9820309d497","head":{"label":"gearmobile:master","ref":"master","sha":"b61f3de54550f1065b888a804026d9820309d497","user":{"login":"gearmobile","id":8580397,"avatar_url":"https://avatars3.githubusercontent.com/u/8580397?v=4","gravatar_id":"","url":"https://api.github.com/users/gearmobile","html_url":"https://github.com/gearmobile","followers_url":"https://api.github.com/users/gearmobile/followers","following_url":"https://api.github.com/users/gearmobile/following{/other_user}","gists_url":"https://api.github.com/users/gearmobile/gists{/gist_id}","starred_url":"https://api.github.com/users/gearmobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gearmobile/subscriptions","organizations_url":"https://api.github.com/users/gearmobile/orgs","repos_url":"https://api.github.com/users/gearmobile/repos","events_url":"https://api.github.com/users/gearmobile/events{/privacy}","received_events_url":"https://api.github.com/users/gearmobile/received_events","type":"User","site_admin":false},"repo":{"id":102043605,"name":"translations","full_name":"gearmobile/translations","owner":{"login":"gearmobile","id":8580397,"avatar_url":"https://avatars3.githubusercontent.com/u/8580397?v=4","gravatar_id":"","url":"https://api.github.com/users/gearmobile","html_url":"https://github.com/gearmobile","followers_url":"https://api.github.com/users/gearmobile/followers","following_url":"https://api.github.com/users/gearmobile/following{/other_user}","gists_url":"https://api.github.com/users/gearmobile/gists{/gist_id}","starred_url":"https://api.github.com/users/gearmobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gearmobile/subscriptions","organizations_url":"https://api.github.com/users/gearmobile/orgs","repos_url":"https://api.github.com/users/gearmobile/repos","events_url":"https://api.github.com/users/gearmobile/events{/privacy}","received_events_url":"https://api.github.com/users/gearmobile/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gearmobile/translations","description":"Переводы от devSchacht","fork":true,"url":"https://api.github.com/repos/gearmobile/translations","forks_url":"https://api.github.com/repos/gearmobile/translations/forks","keys_url":"https://api.github.com/repos/gearmobile/translations/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gearmobile/translations/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gearmobile/translations/teams","hooks_url":"https://api.github.com/repos/gearmobile/translations/hooks","issue_events_url":"https://api.github.com/repos/gearmobile/translations/issues/events{/number}","events_url":"https://api.github.com/repos/gearmobile/translations/events","assignees_url":"https://api.github.com/repos/gearmobile/translations/assignees{/user}","branches_url":"https://api.github.com/repos/gearmobile/translations/branches{/branch}","tags_url":"https://api.github.com/repos/gearmobile/translations/tags","blobs_url":"https://api.github.com/repos/gearmobile/translations/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gearmobile/translations/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gearmobile/translations/git/refs{/sha}","trees_url":"https://api.github.com/repos/gearmobile/translations/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gearmobile/translations/statuses/{sha}","languages_url":"https://api.github.com/repos/gearmobile/translations/languages","stargazers_url":"https://api.github.com/repos/gearmobile/translations/stargazers","contributors_url":"https://api.github.com/repos/gearmobile/translations/contributors","subscribers_url":"https://api.github.com/repos/gearmobile/translations/subscribers","subscription_url":"https://api.github.com/repos/gearmobile/translations/subscription","commits_url":"https://api.github.com/repos/gearmobile/translations/commits{/sha}","git_commits_url":"https://api.github.com/repos/gearmobile/translations/git/commits{/sha}","comments_url":"https://api.github.com/repos/gearmobile/translations/comments{/number}","issue_comment_url":"https://api.github.com/repos/gearmobile/translations/issues/comments{/number}","contents_url":"https://api.github.com/repos/gearmobile/translations/contents/{+path}","compare_url":"https://api.github.com/repos/gearmobile/translations/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gearmobile/translations/merges","archive_url":"https://api.github.com/repos/gearmobile/translations/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gearmobile/translations/downloads","issues_url":"https://api.github.com/repos/gearmobile/translations/issues{/number}","pulls_url":"https://api.github.com/repos/gearmobile/translations/pulls{/number}","milestones_url":"https://api.github.com/repos/gearmobile/translations/milestones{/number}","notifications_url":"https://api.github.com/repos/gearmobile/translations/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gearmobile/translations/labels{/name}","releases_url":"https://api.github.com/repos/gearmobile/translations/releases{/id}","deployments_url":"https://api.github.com/repos/gearmobile/translations/deployments","created_at":"2017-08-31T20:48:56Z","updated_at":"2017-08-30T18:00:55Z","pushed_at":"2018-02-12T11:28:18Z","git_url":"git://github.com/gearmobile/translations.git","ssh_url":"git@github.com:gearmobile/translations.git","clone_url":"https://github.com/gearmobile/translations.git","svn_url":"https://github.com/gearmobile/translations","homepage":"https://medium.com/devschacht","size":39352,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"devSchacht:master","ref":"master","sha":"ba4b0c59ccd19bca632c158be87e0156a3e1465c","user":{"login":"devSchacht","id":26438938,"avatar_url":"https://avatars2.githubusercontent.com/u/26438938?v=4","gravatar_id":"","url":"https://api.github.com/users/devSchacht","html_url":"https://github.com/devSchacht","followers_url":"https://api.github.com/users/devSchacht/followers","following_url":"https://api.github.com/users/devSchacht/following{/other_user}","gists_url":"https://api.github.com/users/devSchacht/gists{/gist_id}","starred_url":"https://api.github.com/users/devSchacht/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/devSchacht/subscriptions","organizations_url":"https://api.github.com/users/devSchacht/orgs","repos_url":"https://api.github.com/users/devSchacht/repos","events_url":"https://api.github.com/users/devSchacht/events{/privacy}","received_events_url":"https://api.github.com/users/devSchacht/received_events","type":"Organization","site_admin":false},"repo":{"id":85697697,"name":"translations","full_name":"devSchacht/translations","owner":{"login":"devSchacht","id":26438938,"avatar_url":"https://avatars2.githubusercontent.com/u/26438938?v=4","gravatar_id":"","url":"https://api.github.com/users/devSchacht","html_url":"https://github.com/devSchacht","followers_url":"https://api.github.com/users/devSchacht/followers","following_url":"https://api.github.com/users/devSchacht/following{/other_user}","gists_url":"https://api.github.com/users/devSchacht/gists{/gist_id}","starred_url":"https://api.github.com/users/devSchacht/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/devSchacht/subscriptions","organizations_url":"https://api.github.com/users/devSchacht/orgs","repos_url":"https://api.github.com/users/devSchacht/repos","events_url":"https://api.github.com/users/devSchacht/events{/privacy}","received_events_url":"https://api.github.com/users/devSchacht/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/devSchacht/translations","description":"Переводы от devSchacht","fork":false,"url":"https://api.github.com/repos/devSchacht/translations","forks_url":"https://api.github.com/repos/devSchacht/translations/forks","keys_url":"https://api.github.com/repos/devSchacht/translations/keys{/key_id}","collaborators_url":"https://api.github.com/repos/devSchacht/translations/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/devSchacht/translations/teams","hooks_url":"https://api.github.com/repos/devSchacht/translations/hooks","issue_events_url":"https://api.github.com/repos/devSchacht/translations/issues/events{/number}","events_url":"https://api.github.com/repos/devSchacht/translations/events","assignees_url":"https://api.github.com/repos/devSchacht/translations/assignees{/user}","branches_url":"https://api.github.com/repos/devSchacht/translations/branches{/branch}","tags_url":"https://api.github.com/repos/devSchacht/translations/tags","blobs_url":"https://api.github.com/repos/devSchacht/translations/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/devSchacht/translations/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/devSchacht/translations/git/refs{/sha}","trees_url":"https://api.github.com/repos/devSchacht/translations/git/trees{/sha}","statuses_url":"https://api.github.com/repos/devSchacht/translations/statuses/{sha}","languages_url":"https://api.github.com/repos/devSchacht/translations/languages","stargazers_url":"https://api.github.com/repos/devSchacht/translations/stargazers","contributors_url":"https://api.github.com/repos/devSchacht/translations/contributors","subscribers_url":"https://api.github.com/repos/devSchacht/translations/subscribers","subscription_url":"https://api.github.com/repos/devSchacht/translations/subscription","commits_url":"https://api.github.com/repos/devSchacht/translations/commits{/sha}","git_commits_url":"https://api.github.com/repos/devSchacht/translations/git/commits{/sha}","comments_url":"https://api.github.com/repos/devSchacht/translations/comments{/number}","issue_comment_url":"https://api.github.com/repos/devSchacht/translations/issues/comments{/number}","contents_url":"https://api.github.com/repos/devSchacht/translations/contents/{+path}","compare_url":"https://api.github.com/repos/devSchacht/translations/compare/{base}...{head}","merges_url":"https://api.github.com/repos/devSchacht/translations/merges","archive_url":"https://api.github.com/repos/devSchacht/translations/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/devSchacht/translations/downloads","issues_url":"https://api.github.com/repos/devSchacht/translations/issues{/number}","pulls_url":"https://api.github.com/repos/devSchacht/translations/pulls{/number}","milestones_url":"https://api.github.com/repos/devSchacht/translations/milestones{/number}","notifications_url":"https://api.github.com/repos/devSchacht/translations/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/devSchacht/translations/labels{/name}","releases_url":"https://api.github.com/repos/devSchacht/translations/releases{/id}","deployments_url":"https://api.github.com/repos/devSchacht/translations/deployments","created_at":"2017-03-21T12:12:36Z","updated_at":"2018-02-11T06:58:57Z","pushed_at":"2018-02-12T11:28:19Z","git_url":"git://github.com/devSchacht/translations.git","ssh_url":"git@github.com:devSchacht/translations.git","clone_url":"https://github.com/devSchacht/translations.git","svn_url":"https://github.com/devSchacht/translations","homepage":"https://medium.com/devschacht","size":39346,"stargazers_count":301,"watchers_count":301,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":66,"mirror_url":null,"archived":false,"open_issues_count":2,"license":null,"forks":66,"open_issues":2,"watchers":301,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/devSchacht/translations/pulls/132"},"html":{"href":"https://github.com/devSchacht/translations/pull/132"},"issue":{"href":"https://api.github.com/repos/devSchacht/translations/issues/132"},"comments":{"href":"https://api.github.com/repos/devSchacht/translations/issues/132/comments"},"review_comments":{"href":"https://api.github.com/repos/devSchacht/translations/pulls/132/comments"},"review_comment":{"href":"https://api.github.com/repos/devSchacht/translations/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/devSchacht/translations/pulls/132/commits"},"statuses":{"href":"https://api.github.com/repos/devSchacht/translations/statuses/b61f3de54550f1065b888a804026d9820309d497"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"gearmobile"}}