{"id":477711,"date":"2026-04-28T07:52:37","date_gmt":"2026-04-28T07:52:37","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=477711"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=477711","title":{"rendered":"\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043a\u0438 SharePoint \u0432 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u043c \u043f\u043e\u0440\u0442\u0430\u043b\u0435: \u043e\u043f\u044b\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Proxy Object Storage \u0434\u043b\u044f \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f No Code \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u00ab\u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u041e\u0431\u044a\u0435\u043a\u0442\u044b\u00bb, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u043c\u0438 \u0431\u0435\u0437 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u2014 Object Definition\u2019\u044b, \u043f\u043e\u043b\u044f, \u0441\u0432\u044f\u0437\u0438, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 UI. \u041d\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0438 \u0436\u0438\u0432\u0443\u0442 \u0432 \u0441\u0430\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u0441\u043c\u0435\u0436\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445, \u0438 \u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u2014 \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445, \u0430 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0435\u0441\u0448\u043e\u0432\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f.<\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u0435\u0439\u0441: \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u043c\u0438\u0433\u0440\u0438\u0440\u0443\u0435\u0442 \u0441 SharePoint Online \u043d\u0430 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434. \u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u2014 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u043e\u043b\u0433\u0438\u0439: \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u0437\u044f\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430 \u043e\u0434\u0438\u043d \u0434\u0435\u043d\u044c. \u041a\u0430\u043a\u043e\u0435-\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0436\u0438\u0432\u0443\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 SharePoint \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438: \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u044b, \u0437\u0430\u044f\u0432\u043a\u0438, \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438, \u043f\u0440\u043e\u0435\u043a\u0442\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b. \u041d\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u0445\u043e\u0447\u0435\u0442, \u0447\u0442\u043e\u0431\u044b <strong>\u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u0440\u0442\u0430\u043b \u043d\u0430 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434<\/strong> \u0441\u0442\u0430\u043b \u0435\u0434\u0438\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430. \u0417\u043d\u0430\u0447\u0438\u0442, \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b SharePoint-\u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0442\u0430\u043a, \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u043e\u043d\u0438 \u2014 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0442\u0430\u043a\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 <strong>Proxy Object Storage<\/strong>, \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u043c\u0438 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0443\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0441 Microsoft Graph API.<\/p>\n<h3>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 Proxy Object Storage \u0432 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434<\/h3>\n<p>\u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 Liferay, \u0438 \u0435\u0433\u043e \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430 Objects \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e <strong>Object Entry Manager<\/strong> \u2014 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0435\u0433\u043e \u0437\u0430 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\u0430. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0437\u0430\u043f\u0438\u0441\u0438 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043e \u0441\u0432\u043e\u0438\u043c <code>storage.type<\/code>.<\/p>\n<p>\u0418\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e \u043c\u044b \u0438 \u0441\u0434\u0435\u043b\u0430\u043b\u0438: \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 <code>ObjectEntryManager<\/code> \u0441 \u0442\u0438\u043f\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 <code>sharepoint<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0435 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 Microsoft Graph API. \u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0438\u0437 \u0434\u0432\u0443\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u043e\u0434\u0443\u043b\u044c<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>object-storage-sp-api<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">API-\u043c\u043e\u0434\u0443\u043b\u044c: \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (tenant ID, client ID, client secret, site URL)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>object-storage-sp-impl<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0418\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f: \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430: \u043e\u0431\u044a\u0435\u043a\u0442\u044b SharePoint \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u2014 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0442\u0435 \u0436\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u043f\u043e\u0438\u0441\u043a \u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e UI-\u043a\u043e\u0434\u0430.<\/p>\n<h3>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430 \u0432 OSGi<\/h3>\n<p>\u0422\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430 \u0441\u043b\u0443\u0436\u0438\u0442 OSGi-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>SharePointObjectEntryManagerImpl<\/code>:<\/p>\n<pre><code class=\"java\">@Component(        property = \"object.entry.manager.storage.type=sharepoint\",        service = ObjectEntryManager.class)public class SharePointObjectEntryManagerImpl        extends BaseObjectEntryManager implements ObjectEntryManager {    \/\/ ...}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0437\u0434\u0435\u0441\u044c \u2014 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>object.entry.manager.storage.type=sharepoint<\/code>. \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0435\u0433\u043e \u0434\u043b\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430. \u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u0451\u0442 Object Definition \u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u043c\u0443 <code>storage.type = \"sharepoint\"<\/code>, \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0448 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442.<\/p>\n<h3>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f: \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a SharePoint \u0447\u0435\u0440\u0435\u0437 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 (Site Settings \u2192 Third Party). \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c \u0441 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f\u043c\u0438 OSGi Metatype:<\/p>\n<pre><code class=\"java\">@ExtendedObjectClassDefinition(        category = \"third-party\",        scope = ExtendedObjectClassDefinition.Scope.GROUP)@Meta.OCD(        id = \"...SharePointConfiguration\",        localization = \"content\/Language\",        name = \"sharepoint-configuration-name\")public interface SharePointConfiguration {    @Meta.AD(name = \"tenant-id\", required = false)    public String tenantId();    @Meta.AD(name = \"client-id\", required = false)    public String clientId();    @Meta.AD(name = \"client-secret\", required = false, type = Meta.Type.Password)    public String clientSecret();    @Meta.AD(name = \"site-url\", required = false)    public String siteUrl();}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 scope <code>GROUP<\/code> \u2014 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u043c \u0441\u0430\u0439\u0442\u0430\u043c \u043f\u043e\u0440\u0442\u0430\u043b\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0440\u0430\u0437\u043d\u044b\u043c SharePoint-\u0441\u0430\u0439\u0442\u0430\u043c.<\/p>\n<h3>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f: OAuth 2.0 Client Credentials<\/h3>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c \u043f\u0440\u0435\u0442\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u2014 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0434\u0430\u043d\u043d\u044b\u043c. SharePoint Online \u0447\u0435\u0440\u0435\u0437 Microsoft Graph API \u0442\u0440\u0435\u0431\u0443\u0435\u0442 OAuth 2.0 \u0442\u043e\u043a\u0435\u043d. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0442\u043e\u043a <code>client_credentials<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u2014 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>\u041a\u043b\u0430\u0441\u0441 <code>SharePointTokenManager<\/code> \u0440\u0435\u0448\u0430\u0435\u0442 \u0434\u0432\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b: \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0438 \u0435\u0433\u043e \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 \u0443\u0447\u0451\u0442\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0436\u0438\u0437\u043d\u0438.<\/p>\n<pre><code class=\"java\">@Component(service = SharePointTokenManager.class)public class SharePointTokenManager {    private final Map&lt;String, CachedToken&gt; tokenCache =        new ConcurrentHashMap&lt;&gt;();    public String getAccessToken(SharePointConfiguration config)        throws Exception {        String cacheKey = config.tenantId() + \"_\" + config.clientId();        CachedToken cachedToken = tokenCache.get(cacheKey);        if ((cachedToken != null) &amp;&amp; !cachedToken.isExpired()) {            return cachedToken.getAccessToken();        }        return fetchNewToken(config, cacheKey);    }    private synchronized String fetchNewToken(            SharePointConfiguration config, String cacheKey)        throws Exception {        \/\/ Double-check \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438        CachedToken cachedToken = tokenCache.get(cacheKey);        if ((cachedToken != null) &amp;&amp; !cachedToken.isExpired()) {            return cachedToken.getAccessToken();        }        String tokenUrl =            \"https:\/\/login.microsoftonline.com\/\" + config.tenantId() +                \"\/oauth2\/v2.0\/token\";        String body =            \"grant_type=client_credentials\" +            \"&amp;client_id=\" + URLCodec.encodeURL(config.clientId()) +            \"&amp;client_secret=\" + URLCodec.encodeURL(config.clientSecret()) +            \"&amp;scope=\" + URLCodec.encodeURL(                \"https:\/\/graph.microsoft.com\/.default\");        \/\/ ... HTTP POST, \u043f\u0430\u0440\u0441\u0438\u043d\u0433 JSON-\u043e\u0442\u0432\u0435\u0442\u0430 ...        int expiresIn = jsonResponse.getInt(\"expires_in\");        \/\/ \u0412\u044b\u0447\u0438\u0442\u0430\u0435\u043c 60 \u0441\u0435\u043a\u0443\u043d\u0434 \u2014 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0437\u0430\u0440\u0430\u043d\u0435\u0435,        \/\/ \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c 401 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f        tokenCache.put(            cacheKey,            new CachedToken(                accessToken,                System.currentTimeMillis() + ((expiresIn - 60) * 1000L)));        \/\/ ...    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e<\/h4>\n<ol>\n<li>\n<p><strong>Double-check locking<\/strong>: \u043c\u0435\u0442\u043e\u0434 <code>fetchNewToken<\/code> \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0430 \u2014 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430. \u0418\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<\/li>\n<li>\n<p><strong>\u0417\u0430\u043f\u0430\u0441 \u0432 60 \u0441\u0435\u043a\u0443\u043d\u0434<\/strong>: \u0442\u043e\u043a\u0435\u043d \u043a\u0435\u0448\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0435 \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 <code>expires_in<\/code>, \u0430 \u0441 \u0432\u044b\u0447\u0435\u0442\u043e\u043c \u043c\u0438\u043d\u0443\u0442\u044b. \u042d\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u0442\u0435\u0447\u0451\u0442 \u0447\u0435\u0440\u0435\u0437 \u0434\u043e\u043b\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u041a\u043b\u044e\u0447 \u043a\u0435\u0448\u0430<\/strong> \u0441\u043e\u0441\u0442\u0430\u0432\u043d\u043e\u0439: <code>tenantId + \"_\" + clientId<\/code> \u2014 \u0442\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 Azure AD \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ol>\n<h4>\u041f\u0440\u0430\u0432\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 Azure AD<\/h4>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Graph API \u0432 Azure AD \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432\u044b\u0434\u0430\u0442\u044c \u0435\u043c\u0443 Application permissions \u0442\u0438\u043f\u0430 <code>Sites.ReadWrite.All<\/code>. \u042d\u0442\u043e \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u043f\u0440\u0430\u0432 <code>Sites.Read.All<\/code> \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0438\u0442\u0430\u0435\u043c \u2014 Graph API \u0442\u0440\u0435\u0431\u0443\u0435\u0442 <code>ReadWrite<\/code> \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438.<\/p>\n<h3>CRUD: \u043a\u0430\u043a \u0441\u043f\u0438\u0441\u043a\u0438 SharePoint \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434<\/h3>\n<h4>\u041c\u0430\u043f\u043f\u0438\u043d\u0433 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439<\/h4>\n<p>\u0421\u0430\u043c\u044b\u0439 \u0432\u0430\u0436\u043d\u044b\u0439 \u044d\u0442\u0430\u043f \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u2014 \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u043c\u0435\u0436\u0434\u0443 \u043c\u0438\u0440\u0430\u043c\u0438:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c SharePoint<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>ObjectDefinition.externalReferenceCode<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0418\u043c\u044f \u0441\u043f\u0438\u0441\u043a\u0430 SharePoint (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>\u041a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u044b<\/code>)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>ObjectField.externalReferenceCode<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0435 \u0438\u043c\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>INN<\/code>, <code>City<\/code>)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>ObjectEntry.externalReferenceCode<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">ID \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0441\u043f\u0438\u0441\u043a\u0430<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>externalReferenceCode<\/code> \u2014 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0435\u043c\u0443 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u0441\u0430\u043c \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u043c\u0438 SharePoint \u0447\u0435\u0440\u0435\u0437 UI, \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u0434 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0445\u0435\u043c\u044b \u0441\u043f\u0438\u0441\u043a\u0430.<\/p>\n<h4>\u0427\u0442\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0441\u043f\u0438\u0441\u043a\u0430<\/h4>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u0430\u043c\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430:<\/p>\n<pre><code class=\"java\">@Overridepublic ObjectEntry getObjectEntry(        long companyId, DTOConverterContext dtoConverterContext,        String externalReferenceCode, ObjectDefinition objectDefinition,        String scopeKey) throws Exception {    \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f    checkPortletResourcePermission(            ActionKeys.VIEW, objectDefinition, scopeKey,            dtoConverterContext.getUser());    SharePointConfiguration config = getSharePointConfiguration(            companyId, getGroupId(objectDefinition, scopeKey));    String listName = objectDefinition.getExternalReferenceCode();    String siteId = resolveSiteId(config);    String endpoint =            GRAPH_API_BASE + \"\/sites\/\" + siteId + \"\/lists\/\" +                    URLCodec.encodeURL(listName) + \"\/items\/\" +                    externalReferenceCode + \"?expand=fields\";    JSONObject responseJSON = executeGraphRequest(            config, endpoint, Http.Method.GET, null);    return toObjectEntry(responseJSON, objectDefinition, dtoConverterContext);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 Graph API \u0438\u043c\u0435\u0435\u0442 \u0432\u0438\u0434:<\/p>\n<pre><code>GET https:\/\/graph.microsoft.com\/v1.0\/sites\/{siteId}\/lists\/{listName}\/items\/{itemId}?expand=fields<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>?expand=fields<\/code> \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u0435\u043d: \u0431\u0435\u0437 \u043d\u0435\u0433\u043e Graph API \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f (id, createdDateTime, etc.).<\/p>\n<h4>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430<\/h4>\n<pre><code class=\"java\">@Overridepublic ObjectEntry addObjectEntry(        DTOConverterContext dtoConverterContext,        ObjectDefinition objectDefinition, ObjectEntry objectEntry,        String scopeKey) throws Exception {    \/\/ \u0418\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u043e\u043b\u044f \u0438\u0437 DTO    Map&lt;String, Object&gt; properties = objectEntry.getProperties();    \/\/ \u0421\u0442\u0440\u043e\u0438\u043c JSON \u0441 \u043f\u043e\u043b\u044f\u043c\u0438    JSONObject fieldsJSON = buildFieldsJSON(properties, objectDefinition);    JSONObject requestJSON = jsonFactory.createJSONObject();    requestJSON.put(\"fields\", fieldsJSON);    String endpoint =        GRAPH_API_BASE + \"\/sites\/\" + siteId + \"\/lists\/\" + listName + \"\/items\";    JSONObject responseJSON = executeGraphRequest(        config, endpoint, Http.Method.POST, requestJSON);    return toObjectEntry(responseJSON, objectDefinition, dtoConverterContext);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435: Graph API \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0433\u0434\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u043e\u0431\u0451\u0440\u043d\u0443\u0442\u044b \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 <code>fields<\/code>:<\/p>\n<pre><code class=\"json\">{  \"fields\": {    \"Title\": \"\u041e\u041e\u041e \u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f\",    \"INN\": \"7701234567\",    \"City\": \"\u041c\u043e\u0441\u043a\u0432\u0430\"  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21161: PATCH-\u0437\u0430\u043f\u0440\u043e\u0441\u044b<\/h3>\n<p>\u041f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 SharePoint \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u043d\u0430 \u0441\u0430\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u0430 \u043d\u0430 \u0435\u0433\u043e \u043f\u043e\u0434\u043e\u0431\u044a\u0435\u043a\u0442 <code>\/fields<\/code>:<\/p>\n<pre><code>PATCH \/sites\/{id}\/lists\/{name}\/items\/{itemId}\/fields<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e. \u041d\u043e \u0432\u044b\u044f\u0441\u043d\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e Liferay-\u043a\u043b\u0430\u0441\u0441 <code>com.liferay.portal.kernel.util.Http<\/code> <strong>\u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 HTTP-\u043c\u0435\u0442\u043e\u0434 PATCH<\/strong>. \u0412 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0438 <code>Http.Method<\/code> \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e GET, POST, PUT, DELETE.<\/p>\n<p>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c Graph API \u043e\u0442\u043a\u043b\u043e\u043d\u044f\u0435\u0442 PUT \u2014 \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u043d\u043d\u043e PATCH. \u041a\u0430\u043a \u0431\u044b\u0442\u044c?<\/p>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0434\u043b\u044f POST \u0438 PATCH \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 <code>java.net.http.HttpClient<\/code> (\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0441 Java 11), \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044f GET \u0438 DELETE \u043d\u0430 Liferay <code>Http<\/code>:<\/p>\n<pre><code class=\"java\">private String _executeRequestViaHttpClient(        String url, String method, String accessToken,        JSONObject requestBody) throws Exception {    String body = (requestBody != null) ?        requestBody.toString() : \"\";    HttpRequest request = HttpRequest.newBuilder()        .uri(URI.create(url))        .method(            method,            HttpRequest.BodyPublishers.ofString(                body, StandardCharsets.UTF_8))        .header(\"Authorization\", \"Bearer \" + accessToken)        .header(\"Accept\", \"application\/json\")        .header(\"Content-Type\", \"application\/json\")        .build();    HttpResponse&lt;String&gt; response = HttpClient.newHttpClient().send(        request, HttpResponse.BodyHandlers.ofString());    return response.body();}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>HttpRequest.Builder.method()<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0435 \u0438\u043c\u044f HTTP-\u043c\u0435\u0442\u043e\u0434\u0430 \u2014 \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 PATCH.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21162: \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0421\u0430\u043c\u044b\u0439 \u043a\u043e\u0432\u0430\u0440\u043d\u044b\u0439 \u0441\u044e\u0440\u043f\u0440\u0438\u0437 \u0436\u0434\u0430\u043b \u043f\u0440\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432. \u041a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0430\u044f offset-\u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f (<code>$top<\/code> + <code>$skip<\/code>) \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 endpoint\u2019\u043e\u0432 Graph API\u2026 \u043d\u043e <strong>\u043d\u0435 \u0434\u043b\u044f list items<\/strong>.<\/p>\n<p>\u0414\u043b\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430 <code>\/sites\/{siteId}\/lists\/{name}\/items<\/code> Microsoft Graph API:<\/p>\n<ul>\n<li>\n<p><strong>\u041d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 <\/strong><code><strong>$skip<\/strong><\/code> \u2014 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0435 \u0434\u0430\u0451\u0442 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e <\/strong><code><strong>$count<\/strong><\/code> \u2014 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 <strong>\u043a\u0443\u0440\u0441\u043e\u0440\u043d\u0443\u044e \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044e<\/strong> \u0447\u0435\u0440\u0435\u0437 <code>@odata.nextLink<\/code><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0438\u043c\u0435\u043d\u043d\u043e offset-\u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044e \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 <code>startPosition<\/code> \u0438 <code>endPosition<\/code>. \u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c?<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u043b\u0438 \u043f\u0440\u0430\u0433\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434:<\/p>\n<pre><code class=\"java\">private void appendPagination(StringBuilder sb, Pagination pagination) {    \/\/ \u0417\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u043d\u0430 1 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b    sb.append(\"&amp;$top=\");    sb.append(pagination.getEndPosition() + 1);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410 \u0432 Java-\u043a\u043e\u0434\u0435 \u043e\u0442\u0440\u0435\u0437\u0430\u0435\u043c \u043d\u0443\u0436\u043d\u044b\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u043e \u043d\u0430\u043b\u0438\u0447\u0438\u044e \u00ab\u043b\u0438\u0448\u043d\u0435\u0433\u043e\u00bb \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430:<\/p>\n<pre><code class=\"java\">int startPosition = pagination.getStartPosition();int endPosition = pagination.getEndPosition();if (allEntries.size() &gt; endPosition) {\/\/ \u0415\u0441\u0442\u044c \u0435\u0449\u0451 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u2014 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442pageEntries = allEntries.subList(startPosition, endPosition);totalCount = endPosition + pagination.getPageSize();} else if (allEntries.size() &gt; startPosition) {pageEntries = allEntries.subList(startPosition, allEntries.size());totalCount = allEntries.size();} else {pageEntries = Collections.emptyList();totalCount = allEntries.size();}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u043a \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043e\u0447\u0435\u0432\u0438\u0434\u0435\u043d: \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0442\u043e\u0447\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0441\u0435\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0446\u0435\u043d\u043a\u0443 \u2014 \u00ab\u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0441\u0442\u043e\u043b\u044c\u043a\u043e-\u0442\u043e\u00bb. \u0414\u043b\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u044d\u0442\u043e \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u044b\u0439 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21163: \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432<\/h3>\n<p>\u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 OData-\u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0434\u043b\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432. Graph API \u0442\u043e\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 OData, \u043d\u043e \u0432 \u0441\u0432\u043e\u0451\u043c \u0434\u0438\u0430\u043b\u0435\u043a\u0442\u0435. \u0420\u0430\u0437\u043b\u0438\u0447\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435:<\/p>\n<ol>\n<li>\n<p><strong>\u0418\u043c\u0435\u043d\u0430 \u043f\u043e\u043b\u0435\u0439<\/strong>: \u0432 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0444\u0438\u043b\u044c\u0442\u0440 \u0441\u0441\u044b\u043b\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u0438\u043c\u0435\u043d\u0430 \u043f\u043e\u043b\u0435\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 (<code>City eq 'Moscow'<\/code>), \u0430 SharePoint \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 <code>fields\/<\/code> (<code>fields\/City eq 'Moscow'<\/code>)<\/p>\n<\/li>\n<li>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u0438<\/strong>: \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 <code>contains()<\/code>, \u0430 Graph API \u0434\u043b\u044f list items \u2014 \u0442\u043e\u043b\u044c\u043a\u043e <code>startsWith()<\/code><\/p>\n<\/li>\n<li>\n<p><strong>Picklist-\u043f\u043e\u043b\u044f<\/strong>: \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u044d\u0442\u043e \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <code>ListTypeEntry<\/code> \u0441 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u043c \u043a\u043b\u044e\u0447\u043e\u043c, \u0430 \u0432 SharePoint \u2014 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>Choice<\/code>-\u043a\u043e\u043b\u043e\u043d\u043a\u0438<\/p>\n<\/li>\n<\/ol>\n<p>\u0414\u043b\u044f \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d <code>ExpressionVisitor<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0445\u043e\u0434\u0438\u0442 AST OData-\u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0438\u0445 \u043d\u0430 \u043b\u0435\u0442\u0443:<\/p>\n<pre><code class=\"java\">public class SharePointQueryExpressionVisitorImpl        implements ExpressionVisitor&lt;Object&gt; {    @Override    public String visitBinaryExpressionOperation(            BinaryExpression.Operation operation,            Object left, Object right) {        \/\/ \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0438\u043c\u044f \u043f\u043e\u043b\u044f: \"City\" \u2192 \"fields\/City\"        ObjectField objectField = objectFieldLocalService.fetchObjectField(                objectDefinitionId, (String)left);        if (objectField != null) {            left = \"fields\/\" + objectField.getExternalReferenceCode();            right = StringUtil.unquote((String)right);            \/\/ \u0414\u043b\u044f Picklist \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u043a\u043b\u044e\u0447 \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 SharePoint            if (objectField.compareBusinessType(\"Picklist\")) {                String spValue = picklistKeyToSharePointValue(                        objectField.getListTypeDefinitionId(), (String)right);                if (spValue != null) {                    right = spValue;                }            }        }        \/\/ \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435: fields\/City eq 'Moscow'        \/\/ ...    }    @Override    public Object visitMethodExpression(            List&lt;Object&gt; expressions, MethodExpression.Type type) {        \/\/ Graph API \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e startsWith \u2014        \/\/ \u043e\u0431\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 (CONTAINS \u0438 STARTS_WITH) \u043c\u0430\u043f\u044f\u0442\u0441\u044f \u043d\u0430 startsWith        if (type == MethodExpression.Type.CONTAINS ||                type == MethodExpression.Type.STARTS_WITH) {            return \"startsWith(\" + fieldName + \", '\" + value + \"')\";        }        throw new UnsupportedOperationException();    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0444\u0430\u0431\u0440\u0438\u043a\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u043a\u0430\u043a OSGi-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0441 \u043a\u043b\u044e\u0447\u043e\u043c <code>sharepoint<\/code>:<\/p>\n<pre><code class=\"java\">@Component(    property = \"filter.factory.key=sharepoint\",    service = FilterFactory.class)public class SharePointFilterFactoryImpl    extends BaseFilterFactory&lt;String&gt; implements FilterFactory&lt;String&gt; {    \/\/ ...}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u0435\u0451 \u043f\u043e \u043a\u043b\u044e\u0447\u0443 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 UI.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21164: \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f SharePoint \u0438 \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u0434\u0430\u0442<\/h3>\n<p>SharePoint \u0445\u0440\u0430\u043d\u0438\u0442 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0435: \u043a\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043b, \u043a\u043e\u0433\u0434\u0430 \u0438\u0437\u043c\u0435\u043d\u0438\u043b, \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442. \u042d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043c\u044b \u0442\u043e\u0436\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c \u0432 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u2014 \u0447\u0435\u0440\u0435\u0437 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f:<\/p>\n<pre><code class=\"java\">\/\/ \u041c\u0430\u043f\u043f\u0438\u043d\u0433 \u0432 toObjectEntry()if (\"createDate\".equals(fieldName)) {value = parseDate(jsonObject.getString(\"createdDateTime\"));        }        else if (\"modifiedDate\".equals(fieldName)) {value = parseDate(jsonObject.getString(\"lastModifiedDateTime\"));        }        else if (\"creator\".equals(fieldName)) {\/\/ createdBy.user.displayNameJSONObject createdBy = jsonObject.getJSONObject(\"createdBy\");    if (createdBy != null) {JSONObject user = createdBy.getJSONObject(\"user\");        if (user != null) {value = user.getString(\"displayName\");        }                }                }                else if (\"url\".equalsIgnoreCase(fieldName)) {value = jsonObject.getString(\"webUrl\");}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u0438 \u0436\u0435 \u043f\u043e\u043b\u044f \u043c\u044b \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u043c \u043a\u0430\u043a <code>read-only<\/code> \u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\/\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u2014 SharePoint \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u043c\u0438 \u0441\u0430\u043c:<\/p>\n<pre><code class=\"java\">private static final Set&lt;String&gt; SYSTEM_FIELD_NAMES =    Set.of(\"createDate\", \"modifiedDate\", \"creator\", \"modifier\", \"url\", \"link\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0449\u0451 \u043e\u0434\u0438\u043d \u043d\u044e\u0430\u043d\u0441 \u2014 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u0442. Graph API \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 timestamp\u2019\u044b \u0432 ISO 8601: <code>2024-01-15T10:30:00Z<\/code>. \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 <code>java.util.Date<\/code>. \u0414\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>SimpleDateFormat<\/code> \u2014 \u043d\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043d\u043e \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0435 \u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435:<\/p>\n<pre><code class=\"java\">private DateFormat getDateFormat() {    return new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss'Z'\");}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21165: \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u043c \u043f\u043e\u043b\u044f\u043c<\/h3>\n<p>\u041f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c \u0441\u043f\u0438\u0441\u043a\u0430 SharePoint \u0447\u0435\u0440\u0435\u0437 Graph API \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043a\u0430\u043a \u043a\u043b\u0430\u0441\u0441. \u041d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043f\u043e\u0440\u0442\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043a\u0430\u0442\u044c. \u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u044d\u043c\u0443\u043b\u044f\u0446\u0438\u044e: \u043f\u0440\u0438 \u0432\u0432\u043e\u0434\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0432\u0441\u0435\u043c \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u043c \u043f\u043e\u043b\u044f\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0438 \u0441\u0442\u0440\u043e\u0438\u043c OR-\u0444\u0438\u043b\u044c\u0442\u0440 \u0438\u0437 <code>startsWith()<\/code>:<\/p>\n<pre><code class=\"java\">private void appendSearch(        StringBuilder sb, ObjectDefinition objectDefinition,        String search) {    StringBuilder searchFilter = new StringBuilder();    for (ObjectField objectField : objectFields) {        if (!objectField.compareBusinessType(\"Text\")) continue;        if (searchFilter.length() &gt; 0) {            searchFilter.append(\" or \");        }        searchFilter.append(                \"startsWith(fields\/\" + spFieldName + \", '\" + search + \"')\");    }    \/\/ \u0412\u0441\u0435\u0433\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u0438\u0441\u043a \u043f\u043e Title    searchFilter.append(\" or startsWith(fields\/Title, '\" + search + \"')\");    \/\/ ...}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u043d\u0430 \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u043c\u0443 \u043f\u043e\u0438\u0441\u043a\u0443, \u043d\u043e \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21166: \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 ID \u0441\u0430\u0439\u0442\u0430<\/h3>\n<p>Graph API \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 \u0441\u0430\u0439\u0442 \u043f\u043e ID (GUID), \u0430 \u043d\u0435 \u043f\u043e URL. \u041d\u043e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0443 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u0435\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0438\u043c\u0435\u043d\u043d\u043e URL \u2014 <code>https:\/\/company.sharepoint.com\/sites\/demo<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u043c\u044b \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c URL \u0432 siteId \u0447\u0435\u0440\u0435\u0437 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 endpoint:<\/p>\n<pre><code class=\"java\">private String resolveSiteId(SharePointConfiguration config)    throws Exception {    URL url = new URL(config.siteUrl());    String hostname = url.getHost();    String path = url.getPath();    String endpoint =        GRAPH_API_BASE + \"\/sites\/\" + hostname + \":\/\" + path;    JSONObject responseJSON = executeGraphRequest(        config, endpoint, Http.Method.GET, null);    String siteId = responseJSON.getString(\"id\");    siteIdCache.put(cacheKey, siteId);    return siteId;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Endpoint \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0441 \u0434\u0432\u043e\u0435\u0442\u043e\u0447\u0438\u0435\u043c: <code>\/sites\/{hostname}:\/{path}<\/code> \u2014 \u044d\u0442\u043e \u0437\u0430\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u0438\u0447\u0430 Graph API \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0441\u0430\u0439\u0442\u0430 \u043f\u043e \u0435\u0433\u043e \u00ab\u0432\u0435\u0431-\u0430\u0434\u0440\u0435\u0441\u0443\u00bb. \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 GUID \u043a\u0435\u0448\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 <code>ConcurrentHashMap<\/code> \u043d\u0430 \u0432\u0441\u0451 \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<h3>\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0432 \u0438\u0442\u043e\u0433\u0435<\/h3>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u0443\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442:<\/p>\n<ol>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c Object Definition \u0432 UI \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0435\u043c\u0443 storage type = <code>sharepoint<\/code><\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0432 \u0438\u0445 \u043a \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u043c SharePoint \u0447\u0435\u0440\u0435\u0437 ERC<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0432\u0438\u0434\u0436\u0435\u0442\u044b \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 (\u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u0444\u0438\u043b\u044c\u0442\u0440\u044b) \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 SharePoint<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043a\u0430\u043a \u0441 \u043e\u0431\u044b\u0447\u043d\u044b\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 \u2014 \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c, \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u043e\u0439 \u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0435\u0439<\/p>\n<\/li>\n<\/ol>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438:<\/p>\n<ul>\n<li>\n<p><strong>Adapter Pattern<\/strong>: \u0432\u0435\u0441\u044c <code>ObjectEntryManager<\/code> \u2014 \u044d\u0442\u043e \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0435\u043b\u044c\u044e \u201c\u041e\u0431\u044a\u0435\u043a\u0442\u0444 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434\u201d \u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API<\/p>\n<\/li>\n<li>\n<p><strong>ERC \u043a\u0430\u043a \u043c\u043e\u0441\u0442<\/strong>: \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 External Reference Code \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043f\u043e\u043b\u0435\u0439 \u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439<\/p>\n<\/li>\n<li>\n<p><strong>\u0424\u0438\u043b\u044c\u0442\u0440\u044b \u0447\u0435\u0440\u0435\u0437 Visitor<\/strong>: \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 OData AST \u0432 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p><strong>\u041a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 double-check locking<\/strong>: \u0434\u043b\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432, site ID \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0440\u0435\u0434\u043a\u043e \u043c\u0435\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 API<\/strong>: \u043a\u043e\u0433\u0434\u0430 \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0443\u0436\u043d\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u2014 \u0438\u0449\u0435\u043c \u043f\u0440\u0430\u0433\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0439 workaround<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1028932\/\">https:\/\/habr.com\/ru\/articles\/1028932\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0412 \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f No Code \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u00ab\u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u041e\u0431\u044a\u0435\u043a\u0442\u044b\u00bb, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u043c\u0438 \u0431\u0435\u0437 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u2014 Object Definition\u2019\u044b, \u043f\u043e\u043b\u044f, \u0441\u0432\u044f\u0437\u0438, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 UI. \u041d\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0438 \u0436\u0438\u0432\u0443\u0442 \u0432 \u0441\u0430\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u0441\u043c\u0435\u0436\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445, \u0438 \u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u2014 \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445, \u0430 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0435\u0441\u0448\u043e\u0432\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f.\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u0435\u0439\u0441: \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u043c\u0438\u0433\u0440\u0438\u0440\u0443\u0435\u0442 \u0441 SharePoint Online \u043d\u0430 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434. \u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u2014 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u043e\u043b\u0433\u0438\u0439: \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u0437\u044f\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430 \u043e\u0434\u0438\u043d \u0434\u0435\u043d\u044c. \u041a\u0430\u043a\u043e\u0435-\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0436\u0438\u0432\u0443\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 SharePoint \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438: \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u044b, \u0437\u0430\u044f\u0432\u043a\u0438, \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438, \u043f\u0440\u043e\u0435\u043a\u0442\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b. \u041d\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u0445\u043e\u0447\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u0440\u0442\u0430\u043b \u043d\u0430 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0441\u0442\u0430\u043b \u0435\u0434\u0438\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430. \u0417\u043d\u0430\u0447\u0438\u0442, \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b SharePoint-\u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0442\u0430\u043a, \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u043e\u043d\u0438 \u2014 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b.\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0442\u0430\u043a\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 Proxy Object Storage, \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u043c\u0438 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0443\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0441 Microsoft Graph API.\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 Proxy Object Storage \u0432 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434\u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 Liferay, \u0438 \u0435\u0433\u043e \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430 Objects \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e Object Entry Manager \u2014 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0435\u0433\u043e \u0437\u0430 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043e\u0431\u044a\u0435\u043a\u0442\u0430. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0437\u0430\u043f\u0438\u0441\u0438 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043e \u0441\u0432\u043e\u0438\u043c storage.type.\u0418\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e \u043c\u044b \u0438 \u0441\u0434\u0435\u043b\u0430\u043b\u0438: \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 ObjectEntryManager \u0441 \u0442\u0438\u043f\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 sharepoint, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0435 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 Microsoft Graph API. \u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0438\u0437 \u0434\u0432\u0443\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439:\u041c\u043e\u0434\u0443\u043b\u044c\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435object-storage-sp-apiAPI-\u043c\u043e\u0434\u0443\u043b\u044c: \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (tenant ID, client ID, client secret, site URL)object-storage-sp-impl\u0418\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f: \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430: \u043e\u0431\u044a\u0435\u043a\u0442\u044b SharePoint \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u2014 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0442\u0435 \u0436\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u043f\u043e\u0438\u0441\u043a \u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e UI-\u043a\u043e\u0434\u0430.\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430 \u0432 OSGi\u0422\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430 \u0441\u043b\u0443\u0436\u0438\u0442 OSGi-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 SharePointObjectEntryManagerImpl:@Component(        property = &#171;object.entry.manager.storage.type=sharepoint&#187;,        service = ObjectEntryManager.class)public class SharePointObjectEntryManagerImpl        extends BaseObjectEntryManager implements ObjectEntryManager {    \/\/ &#8230;}\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0437\u0434\u0435\u0441\u044c \u2014 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e object.entry.manager.storage.type=sharepoint. \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0435\u0433\u043e \u0434\u043b\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430. \u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u0451\u0442 Object Definition \u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u043c\u0443 storage.type = &#171;sharepoint&#187;, \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0448 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442.\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f: \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0430\u0439\u0442\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a SharePoint \u0447\u0435\u0440\u0435\u0437 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434 (Site Settings \u2192 Third Party). \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c \u0441 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f\u043c\u0438 OSGi Metatype:@ExtendedObjectClassDefinition(        category = &#171;third-party&#187;,        scope = ExtendedObjectClassDefinition.Scope.GROUP)@Meta.OCD(        id = &#171;&#8230;SharePointConfiguration&#187;,        localization = &#171;content\/Language&#187;,        name = &#171;sharepoint-configuration-name&#187;)public interface SharePointConfiguration {    @Meta.AD(name = &#171;tenant-id&#187;, required = false)    public String tenantId();    @Meta.AD(name = &#171;client-id&#187;, required = false)    public String clientId();    @Meta.AD(name = &#171;client-secret&#187;, required = false, type = Meta.Type.Password)    public String clientSecret();    @Meta.AD(name = &#171;site-url&#187;, required = false)    public String siteUrl();}\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 scope GROUP \u2014 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u043c \u0441\u0430\u0439\u0442\u0430\u043c \u043f\u043e\u0440\u0442\u0430\u043b\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0440\u0430\u0437\u043d\u044b\u043c SharePoint-\u0441\u0430\u0439\u0442\u0430\u043c.\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f: OAuth 2.0 Client Credentials\u041f\u0435\u0440\u0432\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c \u043f\u0440\u0435\u0442\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u2014 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0434\u0430\u043d\u043d\u044b\u043c. SharePoint Online \u0447\u0435\u0440\u0435\u0437 Microsoft Graph API \u0442\u0440\u0435\u0431\u0443\u0435\u0442 OAuth 2.0 \u0442\u043e\u043a\u0435\u043d. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0442\u043e\u043a client_credentials, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u2014 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.\u041a\u043b\u0430\u0441\u0441 SharePointTokenManager \u0440\u0435\u0448\u0430\u0435\u0442 \u0434\u0432\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b: \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0438 \u0435\u0433\u043e \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 \u0443\u0447\u0451\u0442\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0436\u0438\u0437\u043d\u0438.@Component(service = SharePointTokenManager.class)public class SharePointTokenManager {    private final Map&lt;String, CachedToken&gt; tokenCache =        new ConcurrentHashMap&lt;&gt;();    public String getAccessToken(SharePointConfiguration config)        throws Exception {        String cacheKey = config.tenantId() + &#171;_&#187; + config.clientId();        CachedToken cachedToken = tokenCache.get(cacheKey);        if ((cachedToken != null) &amp;&amp; !cachedToken.isExpired()) {            return cachedToken.getAccessToken();        }        return fetchNewToken(config, cacheKey);    }    private synchronized String fetchNewToken(            SharePointConfiguration config, String cacheKey)        throws Exception {        \/\/ Double-check \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438        CachedToken cachedToken = tokenCache.get(cacheKey);        if ((cachedToken != null) &amp;&amp; !cachedToken.isExpired()) {            return cachedToken.getAccessToken();        }        String tokenUrl =            &#171;https:\/\/login.microsoftonline.com\/&#187; + config.tenantId() +                &#171;\/oauth2\/v2.0\/token&#187;;        String body =            &#171;grant_type=client_credentials&#187; +            &#171;&amp;client_id=&#187; + URLCodec.encodeURL(config.clientId()) +            &#171;&amp;client_secret=&#187; + URLCodec.encodeURL(config.clientSecret()) +            &#171;&amp;scope=&#187; + URLCodec.encodeURL(                &#171;https:\/\/graph.microsoft.com\/.default&#187;);        \/\/ &#8230; HTTP POST, \u043f\u0430\u0440\u0441\u0438\u043d\u0433 JSON-\u043e\u0442\u0432\u0435\u0442\u0430 &#8230;        int expiresIn = jsonResponse.getInt(&#171;expires_in&#187;);        \/\/ \u0412\u044b\u0447\u0438\u0442\u0430\u0435\u043c 60 \u0441\u0435\u043a\u0443\u043d\u0434 \u2014 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0437\u0430\u0440\u0430\u043d\u0435\u0435,        \/\/ \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c 401 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f        tokenCache.put(            cacheKey,            new CachedToken(                accessToken,                System.currentTimeMillis() + ((expiresIn &#8212; 60) * 1000L)));        \/\/ &#8230;    }}\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043eDouble-check locking: \u043c\u0435\u0442\u043e\u0434 fetchNewToken \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0430 \u2014 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430. \u0418\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442 \u0442\u043e\u043a\u0435\u043d.\u0417\u0430\u043f\u0430\u0441 \u0432 60 \u0441\u0435\u043a\u0443\u043d\u0434: \u0442\u043e\u043a\u0435\u043d \u043a\u0435\u0448\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0435 \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 expires_in, \u0430 \u0441 \u0432\u044b\u0447\u0435\u0442\u043e\u043c \u043c\u0438\u043d\u0443\u0442\u044b. \u042d\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u0442\u0435\u0447\u0451\u0442 \u0447\u0435\u0440\u0435\u0437 \u0434\u043e\u043b\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.\u041a\u043b\u044e\u0447 \u043a\u0435\u0448\u0430 \u0441\u043e\u0441\u0442\u0430\u0432\u043d\u043e\u0439: tenantId + &#171;_&#187; + clientId \u2014 \u0442\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u0430\u0439\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 Azure AD \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\u041f\u0440\u0430\u0432\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 Azure AD\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Graph API \u0432 Azure AD \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432\u044b\u0434\u0430\u0442\u044c \u0435\u043c\u0443 Application permissions \u0442\u0438\u043f\u0430 Sites.ReadWrite.All. \u042d\u0442\u043e \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u043f\u0440\u0430\u0432 Sites.Read.All \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0438\u0442\u0430\u0435\u043c \u2014 Graph API \u0442\u0440\u0435\u0431\u0443\u0435\u0442 ReadWrite \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438.CRUD: \u043a\u0430\u043a \u0441\u043f\u0438\u0441\u043a\u0438 SharePoint \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434\u041c\u0430\u043f\u043f\u0438\u043d\u0433 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439\u0421\u0430\u043c\u044b\u0439 \u0432\u0430\u0436\u043d\u044b\u0439 \u044d\u0442\u0430\u043f \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u2014 \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u043c\u0435\u0436\u0434\u0443 \u043c\u0438\u0440\u0430\u043c\u0438:\u041a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f \u0418\u043d\u043a\u043e\u043c\u0430\u043d\u0434\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c SharePointObjectDefinition.externalReferenceCode\u0418\u043c\u044f \u0441\u043f\u0438\u0441\u043a\u0430 SharePoint (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u041a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u044b)ObjectField.externalReferenceCode\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0435 \u0438\u043c\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 INN, City)ObjectEntry.externalReferenceCodeID \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0441\u043f\u0438\u0441\u043a\u0430\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 externalReferenceCode \u2014 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0435\u043c\u0443 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 \u0441\u0430\u043c \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u043c\u0438 SharePoint \u0447\u0435\u0440\u0435\u0437 UI, \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u0434 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0445\u0435\u043c\u044b \u0441\u043f\u0438\u0441\u043a\u0430.\u0427\u0442\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0441\u043f\u0438\u0441\u043a\u0430\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u0430\u043c\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430:@Overridepublic ObjectEntry getObjectEntry(        long companyId, DTOConverterContext dtoConverterContext,        String externalReferenceCode, ObjectDefinition objectDefinition,        String scopeKey) throws Exception {    \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f    checkPortletResourcePermission(            ActionKeys.VIEW, objectDefinition, scopeKey,            dtoConverterContext.getUser());    SharePointConfiguration config = getSharePointConfiguration(            companyId, getGroupId(objectDefinition, scopeKey));    String listName = objectDefinition.getExternalReferenceCode();    String siteId = resolveSiteId(config);    String endpoint =            GRAPH_API_BASE + &#171;\/sites\/&#187; + siteId + &#171;\/lists\/&#187; +                    URLCodec.encodeURL(listName) + &#171;\/items\/&#187; +                    externalReferenceCode + &#171;?expand=fields&#187;;    JSONObject responseJSON = executeGraphRequest(            config, endpoint, Http.Method.GET, null);    return toObjectEntry(responseJSON, objectDefinition, dtoConverterContext);}\u041a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 Graph API \u0438\u043c\u0435\u0435\u0442 \u0432\u0438\u0434:GET https:\/\/graph.microsoft.com\/v1.0\/sites\/{siteId}\/lists\/{listName}\/items\/{itemId}?expand=fields\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 ?expand=fields \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u0435\u043d: \u0431\u0435\u0437 \u043d\u0435\u0433\u043e Graph API \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f (id, createdDateTime, etc.).\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430@Overridepublic ObjectEntry addObjectEntry(        DTOConverterContext dtoConverterContext,        ObjectDefinition objectDefinition, ObjectEntry objectEntry,        String scopeKey) throws Exception {    \/\/ \u0418\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u043e\u043b\u044f \u0438\u0437 DTO    Map&lt;String, Object&gt; properties = objectEntry.getProperties();    \/\/ \u0421\u0442\u0440\u043e\u0438\u043c JSON \u0441 \u043f\u043e\u043b\u044f\u043c\u0438    JSONObject fieldsJSON = buildFieldsJSON(properties, objectDefinition);    JSONObject requestJSON = jsonFactory.createJSONObject();    requestJSON.put(&#171;fields&#187;, fieldsJSON);    String endpoint =        GRAPH_API_BASE + &#171;\/sites\/&#187; + siteId + &#171;\/lists\/&#187; + listName + &#171;\/items&#187;;    JSONObject responseJSON = executeGraphRequest(        config, endpoint, Http.Method.POST, requestJSON);    return toObjectEntry(responseJSON, objectDefinition, dtoConverterContext);}\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435: Graph API \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0433\u0434\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u043e\u0431\u0451\u0440\u043d\u0443\u0442\u044b \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 fields:{  &#171;fields&#187;: {    &#171;Title&#187;: &#171;\u041e\u041e\u041e \u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f&#187;,    &#171;INN&#187;: &#171;7701234567&#187;,    &#171;City&#187;: &#171;\u041c\u043e\u0441\u043a\u0432\u0430&#187;  }}\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21161: PATCH-\u0437\u0430\u043f\u0440\u043e\u0441\u044b\u041f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 SharePoint \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u043d\u0430 \u0441\u0430\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u0430 \u043d\u0430 \u0435\u0433\u043e \u043f\u043e\u0434\u043e\u0431\u044a\u0435\u043a\u0442 \/fields:PATCH \/sites\/{id}\/lists\/{name}\/items\/{itemId}\/fields\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-477711","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/477711","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=477711"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/477711\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=477711"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=477711"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=477711"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}