{"action":"created","comment":{"url":"https://api.github.com/repos/tananaev/traccar/pulls/comments/128739669","pull_request_review_id":51448186,"id":128739669,"diff_hunk":"@@ -501,224 +268,52 @@ public void addStatistics(Statistics statistics) throws SQLException {\n                 .executeUpdate());\n     }\n \n-    public Collection<Calendar> getCalendars() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectCalendarsAll\"))\n-                .executeQuery(Calendar.class);\n+    public static String makeName(String object) {\n+        return object.substring(0, 1).toUpperCase() + object.replace(\"Id\", \"\").substring(1);\n     }\n \n-    public void addCalendar(Calendar calendar) throws SQLException {\n-        calendar.setId(QueryBuilder.create(dataSource, getQuery(\"database.insertCalendar\"), true)\n-                .setObject(calendar)\n-                .executeUpdate());\n-    }\n-\n-    public void updateCalendar(Calendar calendar) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.updateCalendar\"))\n-                .setObject(calendar)\n-                .executeUpdate();\n-    }\n-\n-    public void removeCalendar(long calendarId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.deleteCalendar\"))\n-                .setLong(\"id\", calendarId)\n-                .executeUpdate();\n+    public static String makeNameId(String object) {\n+        return object.substring(0, 1).toLowerCase() + object.substring(1) + (object.indexOf(\"Id\") == -1 ? \"Id\" : \"\");\n     }\n \n-    public Collection<CalendarPermission> getCalendarPermissions() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectCalendarPermissions\"))\n-                .executeQuery(CalendarPermission.class);\n-    }\n+    public void linkObject(String owner, long ownerId, String property, long propertyId,\n+            boolean link) throws SQLException {\n+        String query = \"database.\" + (!link ? \"un\" : \"\") + \"link\" + makeName(owner) + makeName(property);\n+        QueryBuilder queryBuilder = QueryBuilder.create(dataSource, getQuery(query));\n \n-    public void linkCalendar(long userId, long calendarId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkCalendar\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"calendarId\", calendarId)\n-                .executeUpdate();\n+        queryBuilder.setLong(makeNameId(owner), ownerId);\n+        queryBuilder.setLong(makeNameId(property), propertyId);\n+        queryBuilder.executeUpdate();\n     }\n \n-    public void unlinkCalendar(long userId, long calendarId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkCalendar\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"calendarId\", calendarId)\n-                .executeUpdate();\n+    public <T> Collection<T> getObjects(Class<T> clazz) throws SQLException {\n+        String query = \"database.select\" + clazz.getSimpleName() + \"s\";\n+        return QueryBuilder.create(dataSource, getQuery(query)).executeQuery(clazz);\n     }\n \n-    public Collection<UserPermission> getUserPermissions() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectUserPermissions\"))\n-                .executeQuery(UserPermission.class);\n+    public Collection<Map<String, Long>> getPermissions(String owner, String property) throws SQLException {\n+        String query = \"database.select\" + makeName(owner) + makeName(property) + \"s\";\n+        return QueryBuilder.create(dataSource, getQuery(query)).executeMapQuery(Long.class);\n     }\n \n-    public void linkUser(long userId, long managedUserId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkUser\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"managedUserId\", managedUserId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkUser(long userId, long managedUserId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkUser\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"managedUserId\", managedUserId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<Attribute> getAttributes() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectAttributes\"))\n-                .executeQuery(Attribute.class);\n-    }\n-\n-    public void addAttribute(Attribute attribute) throws SQLException {\n-        attribute.setId(QueryBuilder.create(dataSource, getQuery(\"database.insertAttribute\"), true)\n-                .setObject(attribute)\n+    public void addObject(BaseModel entity) throws SQLException {\n+        String query = \"database.insert\" + entity.getClass().getSimpleName();\n+        entity.setId(QueryBuilder.create(dataSource, getQuery(query), true)\n+                .setObject(entity)\n                 .executeUpdate());\n     }\n \n-    public void updateAttribute(Attribute attribute) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.updateAttribute\"))\n-                .setObject(attribute)\n+    public void updateObject(BaseModel entity) throws SQLException {\n+        String query = \"database.update\" + entity.getClass().getSimpleName();\n+        QueryBuilder.create(dataSource, getQuery(query))\n+                .setObject(entity)\n                 .executeUpdate();\n     }\n \n-    public void removeAttribute(long computedAttributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.deleteAttribute\"))\n-                .setLong(\"id\", computedAttributeId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<AttributePermission> getAttributePermissions() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectAttributePermissions\"))\n-                .executeQuery(AttributePermission.class);\n-    }\n-\n-    public void linkAttribute(long userId, long attributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkAttribute\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"attributeId\", attributeId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkAttribute(long userId, long attributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkAttribute\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"attributeId\", attributeId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<GroupAttribute> getGroupAttributes() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectGroupAttributes\"))\n-                .executeQuery(GroupAttribute.class);\n-    }\n-\n-    public void linkGroupAttribute(long groupId, long attributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkGroupAttribute\"))\n-                .setLong(\"groupId\", groupId)\n-                .setLong(\"attributeId\", attributeId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkGroupAttribute(long groupId, long attributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkGroupAttribute\"))\n-                .setLong(\"groupId\", groupId)\n-                .setLong(\"attributeId\", attributeId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<DeviceAttribute> getDeviceAttributes() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectDeviceAttributes\"))\n-                .executeQuery(DeviceAttribute.class);\n-    }\n-\n-    public void linkDeviceAttribute(long deviceId, long attributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkDeviceAttribute\"))\n-                .setLong(\"deviceId\", deviceId)\n-                .setLong(\"attributeId\", attributeId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkDeviceAttribute(long deviceId, long attributeId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkDeviceAttribute\"))\n-                .setLong(\"deviceId\", deviceId)\n-                .setLong(\"attributeId\", attributeId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<Driver> getDrivers() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectDrivers\"))\n-                .executeQuery(Driver.class);\n-    }\n-\n-    public void addDriver(Driver driver) throws SQLException {\n-        driver.setId(QueryBuilder.create(dataSource, getQuery(\"database.insertDriver\"), true)\n-                .setObject(driver)\n-                .executeUpdate());\n-    }\n-\n-    public void updateDriver(Driver driver) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.updateDriver\"))\n-                .setObject(driver)\n-                .executeUpdate();\n-    }\n-\n-    public void removeDriver(long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.deleteDriver\"))\n-                .setLong(\"id\", driverId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<DriverPermission> getDriverPermissions() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectDriverPermissions\"))\n-                .executeQuery(DriverPermission.class);\n-    }\n-\n-    public void linkDriver(long userId, long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkDriver\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"driverId\", driverId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkDriver(long userId, long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkDriver\"))\n-                .setLong(\"userId\", userId)\n-                .setLong(\"driverId\", driverId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<GroupDriver> getGroupDrivers() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectGroupDrivers\"))\n-                .executeQuery(GroupDriver.class);\n-    }\n-\n-    public void linkGroupDriver(long groupId, long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkGroupDriver\"))\n-                .setLong(\"groupId\", groupId)\n-                .setLong(\"driverId\", driverId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkGroupDriver(long groupId, long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkGroupDriver\"))\n-                .setLong(\"groupId\", groupId)\n-                .setLong(\"driverId\", driverId)\n-                .executeUpdate();\n-    }\n-\n-    public Collection<DeviceDriver> getDeviceDrivers() throws SQLException {\n-        return QueryBuilder.create(dataSource, getQuery(\"database.selectDeviceDrivers\"))\n-                .executeQuery(DeviceDriver.class);\n-    }\n-\n-    public void linkDeviceDriver(long deviceId, long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.linkDeviceDriver\"))\n-                .setLong(\"deviceId\", deviceId)\n-                .setLong(\"driverId\", driverId)\n-                .executeUpdate();\n-    }\n-\n-    public void unlinkDeviceDriver(long deviceId, long driverId) throws SQLException {\n-        QueryBuilder.create(dataSource, getQuery(\"database.unlinkDeviceDriver\"))\n-                .setLong(\"deviceId\", deviceId)\n-                .setLong(\"driverId\", driverId)\n+    public void removeObject(Class<? extends BaseModel> clazz, long entityId) throws SQLException {\n+        String query = \"database.delete\" + clazz.getSimpleName();","path":"src/org/traccar/database/DataManager.java","position":545,"original_position":545,"commit_id":"55d91ec7a86777bb0427422c15c2b8a60aa00071","original_commit_id":"55d91ec7a86777bb0427422c15c2b8a60aa00071","user":{"login":"Abyss777","id":5688080,"avatar_url":"https://avatars2.githubusercontent.com/u/5688080?v=4","gravatar_id":"","url":"https://api.github.com/users/Abyss777","html_url":"https://github.com/Abyss777","followers_url":"https://api.github.com/users/Abyss777/followers","following_url":"https://api.github.com/users/Abyss777/following{/other_user}","gists_url":"https://api.github.com/users/Abyss777/gists{/gist_id}","starred_url":"https://api.github.com/users/Abyss777/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Abyss777/subscriptions","organizations_url":"https://api.github.com/users/Abyss777/orgs","repos_url":"https://api.github.com/users/Abyss777/repos","events_url":"https://api.github.com/users/Abyss777/events{/privacy}","received_events_url":"https://api.github.com/users/Abyss777/received_events","type":"User","site_admin":false},"body":"Please, take a look at `BaseResource.handlePermission()` function, it accepts Map {\"deviceId:2\", \"driverId\":4}\r\nWe need to call `PermissionManager.checkPermission(object, userId, objectId)` where object should be replaced to class and call `Database.linkObject(owner, ownerId, property, propertyId, link)` where owner and property should be replaced to class.","created_at":"2017-07-21T11:19:02Z","updated_at":"2017-07-21T11:19:02Z","html_url":"https://github.com/tananaev/traccar/pull/3373#discussion_r128739669","pull_request_url":"https://api.github.com/repos/tananaev/traccar/pulls/3373","_links":{"self":{"href":"https://api.github.com/repos/tananaev/traccar/pulls/comments/128739669"},"html":{"href":"https://github.com/tananaev/traccar/pull/3373#discussion_r128739669"},"pull_request":{"href":"https://api.github.com/repos/tananaev/traccar/pulls/3373"}}},"pull_request":{"url":"https://api.github.com/repos/tananaev/traccar/pulls/3373","id":131470976,"html_url":"https://github.com/tananaev/traccar/pull/3373","diff_url":"https://github.com/tananaev/traccar/pull/3373.diff","patch_url":"https://github.com/tananaev/traccar/pull/3373.patch","issue_url":"https://api.github.com/repos/tananaev/traccar/issues/3373","number":3373,"state":"open","locked":false,"title":"Refactor managers, permissions etc.","user":{"login":"Abyss777","id":5688080,"avatar_url":"https://avatars2.githubusercontent.com/u/5688080?v=4","gravatar_id":"","url":"https://api.github.com/users/Abyss777","html_url":"https://github.com/Abyss777","followers_url":"https://api.github.com/users/Abyss777/followers","following_url":"https://api.github.com/users/Abyss777/following{/other_user}","gists_url":"https://api.github.com/users/Abyss777/gists{/gist_id}","starred_url":"https://api.github.com/users/Abyss777/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Abyss777/subscriptions","organizations_url":"https://api.github.com/users/Abyss777/orgs","repos_url":"https://api.github.com/users/Abyss777/repos","events_url":"https://api.github.com/users/Abyss777/events{/privacy}","received_events_url":"https://api.github.com/users/Abyss777/received_events","type":"User","site_admin":false},"body":"It is extremely big change. I'm not sure if it is possible to review well. Ask me about any doubts.\r\n\r\nMain changes:\r\n- Implemented `SimpleObjectManager` that serves set of objects and links to users (Calendars)\r\n- Implemented `ExtendedObjectManager` that also serves links to devices and groups (Geofences, Drivers, Attributes)\r\n- Implemented `getObjects`, `addObject`, `updateObject`, `removeObject` database functions, they used Class name to select request string.\r\n- Some `database.*` names was changed to consistence\r\n- Changed permissions model hierarchy to fit new managers model. Added interface with `getSlaveId` function.\r\n- Combined permissions resources. Unfortunately had to combine devices and groups permissions in its main resource, because `@Delete` regex matching failed if it is in different resources.\r\n- Implemented smart functions to check and refresh permissions\r\n\r\nPS: I believe new managers model might be used to optimize users, devices and groups management.","created_at":"2017-07-20T05:11:59Z","updated_at":"2017-07-21T11:19:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b7ccb5675a65c9339135db5c4d7b0f7572045a46","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/tananaev/traccar/pulls/3373/commits","review_comments_url":"https://api.github.com/repos/tananaev/traccar/pulls/3373/comments","review_comment_url":"https://api.github.com/repos/tananaev/traccar/pulls/comments{/number}","comments_url":"https://api.github.com/repos/tananaev/traccar/issues/3373/comments","statuses_url":"https://api.github.com/repos/tananaev/traccar/statuses/55d91ec7a86777bb0427422c15c2b8a60aa00071","head":{"label":"Abyss777:refactor_managers","ref":"refactor_managers","sha":"55d91ec7a86777bb0427422c15c2b8a60aa00071","user":{"login":"Abyss777","id":5688080,"avatar_url":"https://avatars2.githubusercontent.com/u/5688080?v=4","gravatar_id":"","url":"https://api.github.com/users/Abyss777","html_url":"https://github.com/Abyss777","followers_url":"https://api.github.com/users/Abyss777/followers","following_url":"https://api.github.com/users/Abyss777/following{/other_user}","gists_url":"https://api.github.com/users/Abyss777/gists{/gist_id}","starred_url":"https://api.github.com/users/Abyss777/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Abyss777/subscriptions","organizations_url":"https://api.github.com/users/Abyss777/orgs","repos_url":"https://api.github.com/users/Abyss777/repos","events_url":"https://api.github.com/users/Abyss777/events{/privacy}","received_events_url":"https://api.github.com/users/Abyss777/received_events","type":"User","site_admin":false},"repo":{"id":58906096,"name":"traccar","full_name":"Abyss777/traccar","owner":{"login":"Abyss777","id":5688080,"avatar_url":"https://avatars2.githubusercontent.com/u/5688080?v=4","gravatar_id":"","url":"https://api.github.com/users/Abyss777","html_url":"https://github.com/Abyss777","followers_url":"https://api.github.com/users/Abyss777/followers","following_url":"https://api.github.com/users/Abyss777/following{/other_user}","gists_url":"https://api.github.com/users/Abyss777/gists{/gist_id}","starred_url":"https://api.github.com/users/Abyss777/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Abyss777/subscriptions","organizations_url":"https://api.github.com/users/Abyss777/orgs","repos_url":"https://api.github.com/users/Abyss777/repos","events_url":"https://api.github.com/users/Abyss777/events{/privacy}","received_events_url":"https://api.github.com/users/Abyss777/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Abyss777/traccar","description":"Traccar GPS Tracking System","fork":true,"url":"https://api.github.com/repos/Abyss777/traccar","forks_url":"https://api.github.com/repos/Abyss777/traccar/forks","keys_url":"https://api.github.com/repos/Abyss777/traccar/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Abyss777/traccar/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Abyss777/traccar/teams","hooks_url":"https://api.github.com/repos/Abyss777/traccar/hooks","issue_events_url":"https://api.github.com/repos/Abyss777/traccar/issues/events{/number}","events_url":"https://api.github.com/repos/Abyss777/traccar/events","assignees_url":"https://api.github.com/repos/Abyss777/traccar/assignees{/user}","branches_url":"https://api.github.com/repos/Abyss777/traccar/branches{/branch}","tags_url":"https://api.github.com/repos/Abyss777/traccar/tags","blobs_url":"https://api.github.com/repos/Abyss777/traccar/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Abyss777/traccar/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Abyss777/traccar/git/refs{/sha}","trees_url":"https://api.github.com/repos/Abyss777/traccar/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Abyss777/traccar/statuses/{sha}","languages_url":"https://api.github.com/repos/Abyss777/traccar/languages","stargazers_url":"https://api.github.com/repos/Abyss777/traccar/stargazers","contributors_url":"https://api.github.com/repos/Abyss777/traccar/contributors","subscribers_url":"https://api.github.com/repos/Abyss777/traccar/subscribers","subscription_url":"https://api.github.com/repos/Abyss777/traccar/subscription","commits_url":"https://api.github.com/repos/Abyss777/traccar/commits{/sha}","git_commits_url":"https://api.github.com/repos/Abyss777/traccar/git/commits{/sha}","comments_url":"https://api.github.com/repos/Abyss777/traccar/comments{/number}","issue_comment_url":"https://api.github.com/repos/Abyss777/traccar/issues/comments{/number}","contents_url":"https://api.github.com/repos/Abyss777/traccar/contents/{+path}","compare_url":"https://api.github.com/repos/Abyss777/traccar/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Abyss777/traccar/merges","archive_url":"https://api.github.com/repos/Abyss777/traccar/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Abyss777/traccar/downloads","issues_url":"https://api.github.com/repos/Abyss777/traccar/issues{/number}","pulls_url":"https://api.github.com/repos/Abyss777/traccar/pulls{/number}","milestones_url":"https://api.github.com/repos/Abyss777/traccar/milestones{/number}","notifications_url":"https://api.github.com/repos/Abyss777/traccar/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Abyss777/traccar/labels{/name}","releases_url":"https://api.github.com/repos/Abyss777/traccar/releases{/id}","deployments_url":"https://api.github.com/repos/Abyss777/traccar/deployments","created_at":"2016-05-16T05:29:28Z","updated_at":"2016-05-16T05:29:30Z","pushed_at":"2017-07-21T08:11:54Z","git_url":"git://github.com/Abyss777/traccar.git","ssh_url":"git@github.com:Abyss777/traccar.git","clone_url":"https://github.com/Abyss777/traccar.git","svn_url":"https://github.com/Abyss777/traccar","homepage":"https://www.traccar.org","size":10339,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"tananaev:master","ref":"master","sha":"d7d66e3b724cde1a681e5e7f24e930d2bddfe703","user":{"login":"tananaev","id":1161863,"avatar_url":"https://avatars3.githubusercontent.com/u/1161863?v=4","gravatar_id":"","url":"https://api.github.com/users/tananaev","html_url":"https://github.com/tananaev","followers_url":"https://api.github.com/users/tananaev/followers","following_url":"https://api.github.com/users/tananaev/following{/other_user}","gists_url":"https://api.github.com/users/tananaev/gists{/gist_id}","starred_url":"https://api.github.com/users/tananaev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tananaev/subscriptions","organizations_url":"https://api.github.com/users/tananaev/orgs","repos_url":"https://api.github.com/users/tananaev/repos","events_url":"https://api.github.com/users/tananaev/events{/privacy}","received_events_url":"https://api.github.com/users/tananaev/received_events","type":"User","site_admin":false},"repo":{"id":4038949,"name":"traccar","full_name":"tananaev/traccar","owner":{"login":"tananaev","id":1161863,"avatar_url":"https://avatars3.githubusercontent.com/u/1161863?v=4","gravatar_id":"","url":"https://api.github.com/users/tananaev","html_url":"https://github.com/tananaev","followers_url":"https://api.github.com/users/tananaev/followers","following_url":"https://api.github.com/users/tananaev/following{/other_user}","gists_url":"https://api.github.com/users/tananaev/gists{/gist_id}","starred_url":"https://api.github.com/users/tananaev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tananaev/subscriptions","organizations_url":"https://api.github.com/users/tananaev/orgs","repos_url":"https://api.github.com/users/tananaev/repos","events_url":"https://api.github.com/users/tananaev/events{/privacy}","received_events_url":"https://api.github.com/users/tananaev/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tananaev/traccar","description":"Traccar GPS Tracking System","fork":false,"url":"https://api.github.com/repos/tananaev/traccar","forks_url":"https://api.github.com/repos/tananaev/traccar/forks","keys_url":"https://api.github.com/repos/tananaev/traccar/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tananaev/traccar/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tananaev/traccar/teams","hooks_url":"https://api.github.com/repos/tananaev/traccar/hooks","issue_events_url":"https://api.github.com/repos/tananaev/traccar/issues/events{/number}","events_url":"https://api.github.com/repos/tananaev/traccar/events","assignees_url":"https://api.github.com/repos/tananaev/traccar/assignees{/user}","branches_url":"https://api.github.com/repos/tananaev/traccar/branches{/branch}","tags_url":"https://api.github.com/repos/tananaev/traccar/tags","blobs_url":"https://api.github.com/repos/tananaev/traccar/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tananaev/traccar/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tananaev/traccar/git/refs{/sha}","trees_url":"https://api.github.com/repos/tananaev/traccar/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tananaev/traccar/statuses/{sha}","languages_url":"https://api.github.com/repos/tananaev/traccar/languages","stargazers_url":"https://api.github.com/repos/tananaev/traccar/stargazers","contributors_url":"https://api.github.com/repos/tananaev/traccar/contributors","subscribers_url":"https://api.github.com/repos/tananaev/traccar/subscribers","subscription_url":"https://api.github.com/repos/tananaev/traccar/subscription","commits_url":"https://api.github.com/repos/tananaev/traccar/commits{/sha}","git_commits_url":"https://api.github.com/repos/tananaev/traccar/git/commits{/sha}","comments_url":"https://api.github.com/repos/tananaev/traccar/comments{/number}","issue_comment_url":"https://api.github.com/repos/tananaev/traccar/issues/comments{/number}","contents_url":"https://api.github.com/repos/tananaev/traccar/contents/{+path}","compare_url":"https://api.github.com/repos/tananaev/traccar/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tananaev/traccar/merges","archive_url":"https://api.github.com/repos/tananaev/traccar/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tananaev/traccar/downloads","issues_url":"https://api.github.com/repos/tananaev/traccar/issues{/number}","pulls_url":"https://api.github.com/repos/tananaev/traccar/pulls{/number}","milestones_url":"https://api.github.com/repos/tananaev/traccar/milestones{/number}","notifications_url":"https://api.github.com/repos/tananaev/traccar/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tananaev/traccar/labels{/name}","releases_url":"https://api.github.com/repos/tananaev/traccar/releases{/id}","deployments_url":"https://api.github.com/repos/tananaev/traccar/deployments","created_at":"2012-04-16T08:33:49Z","updated_at":"2017-07-21T09:28:06Z","pushed_at":"2017-07-21T08:11:57Z","git_url":"git://github.com/tananaev/traccar.git","ssh_url":"git@github.com:tananaev/traccar.git","clone_url":"https://github.com/tananaev/traccar.git","svn_url":"https://github.com/tananaev/traccar","homepage":"https://www.traccar.org","size":10314,"stargazers_count":995,"watchers_count":995,"language":"Java","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":837,"mirror_url":null,"open_issues_count":1202,"forks":837,"open_issues":1202,"watchers":995,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/tananaev/traccar/pulls/3373"},"html":{"href":"https://github.com/tananaev/traccar/pull/3373"},"issue":{"href":"https://api.github.com/repos/tananaev/traccar/issues/3373"},"comments":{"href":"https://api.github.com/repos/tananaev/traccar/issues/3373/comments"},"review_comments":{"href":"https://api.github.com/repos/tananaev/traccar/pulls/3373/comments"},"review_comment":{"href":"https://api.github.com/repos/tananaev/traccar/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/tananaev/traccar/pulls/3373/commits"},"statuses":{"href":"https://api.github.com/repos/tananaev/traccar/statuses/55d91ec7a86777bb0427422c15c2b8a60aa00071"}}}}
{"actor":{"display_login":"Abyss777"}}