需要继续前进
开始 快开始这是通往工作最短的路, 人均谷歌驱动器连接.
这个纳米提供了可重复使用的 Rails 引擎和前端包,用于连接 a Neeto产品资源到Google Drive.
所有者及其出口资源的例子:
| 产品 | 整合拥有者 | 导出资源 |
|---|---|---|
| 需要论坛 | Form | Submission |
| 需要卡尔 | Meeting | Booking |
每个所有者都有自己的Google账户连接. Google Drive 证书为 不与 Google 工作表或另一个所有者共享 .
纳米能提供什么
后端引擎
铁路机车拥有:
- Google OAuth 与 图片
drive.file财务报告和审定财务报表userinfo.email瞄准镜. - 加密了访问键和刷新键存储 .
- 共有者连接状态,账户细节,设置,以及主文件夹ID.
- Google Drive 文件,文件夹,上传,查询,以及令牌-新鲜操作.
- 连接、配置、断开和 OAuth 回调API 端点.
它也处理这些边缘案件:
- 它只在重接同名 Google 时重新使用省略的刷新符号 账户.
- 它在存储的文件夹被删除或被丢弃时创建一个新的主文件夹 .
- 它同步了在Google Drive中直接更改的文件夹名.
- 它防止同时提出的请求留下重复的主文件夹.
- 它断开无效的授权而不删除现有的 Drive 文件 .
前端软件包
前端套件导出 :
import {
GoogleDriveIcon,
GoogleDriveIntegration,
GoogleDriveSettings,
useGoogleDrive,
} from "@bigbinary/neeto-google-drive-frontend";
连接 UI 包括:
- 与谷歌签约.
- 连通账户邮件.
- 复制并打开主文件夹链接 .
- 配置和断开动作 .
- OAuth 和授权错误消息 .
设置用户界面包括:
- 主文件夹名.
- 资源文件夹切换和命名-字段构建器 .
- 保存、取消、加载和缺失整合状态.
- 可选
renderAdditionalSettings用于主机自主选项的插槽 .
产品特定设置, 如附件字段选择或 PDF 导出
切换是 没有 内置纳米。 主持人让他们通过
renderAdditionalSettings 并将其值存储在 additional_settings。 。 。 .
东道产品必须提供什么
纳米不知道表格提交或日历预订是如何工作的。 东道主 产品拥有 :
- 租户范围房主查询及授权.
- 产品特定命名字段和任何额外的设置 UI.
- 决定何时导出的背景工作 .
- 资源格式化,附件检索,和PDF生成.
- 产品路线,页面布局,面包屑,帮助链接,以及生命周期清理.
纳米没有重新连接的电子邮件 。 一个主机可以加一个,但它不是 综合工作所需的经费.
快开始
1. 安装后端引擎
添加宝石 :
source "NEETO_GEM_SERVER_URL" do
gem "neeto-google-drive-engine"
end
安装它 :
bundle install
将引擎挂入 config/routes.rb数字 :
Rails.application.routes.draw do
mount NeetoGoogleDriveEngine::Engine, at: "/neeto_google_drive"
end
复制并运行迁移 :
bundle exec rails neeto_google_drive_engine:install:migrations
bundle exec rails db:migrate
2. 配置谷歌 OAuth
创建 config/initializers/neeto_google_drive_engine.rb数字 :
# frozen_string_literal: true
NeetoGoogleDriveEngine.application_name = "NeetoForm"
NeetoGoogleDriveEngine.oauth_app_url =
Rails.application.vault.google_drive[:oauth_app_url]
NeetoGoogleDriveEngine.oauth_client_id =
Rails.application.vault.google_drive[:client_id]
NeetoGoogleDriveEngine.oauth_client_secret =
Rails.application.vault.google_drive[:client_secret]
NeetoGoogleDriveEngine.oauth_state_private_key =
Rails.application.vault.jwt[:connect_private_key]
NeetoGoogleDriveEngine.oauth_state_public_key =
Rails.application.vault.jwt[:connect_public_key]
NeetoGoogleDriveEngine.user_class = "User"
在 Google OAuth 客户端中注册此完整重定向 URI :
<oauth_app_url>/neeto_google_drive/api/v1/oauth
例如:
https://connect.neetoform.net/neeto_google_drive/api/v1/oauth
使用专门的Google Drive OAuth配置. 不重复使用谷歌工作表 由所有者共享 Drive 证书.
OAuth州与RS256签署,两小时后失效. 回电 拒绝无效、已过期或租户无法使用的所有者数据.
3. 增加所有者合同
包含 NeetoGoogleDriveEngine::HasGoogleDrive 在拥有该模型的模型中
连接 :
class Form < ApplicationRecord
include NeetoGoogleDriveEngine::HasGoogleDrive
include GoogleDriveIntegrable
end
主机适配器可以执行这些方法:
module GoogleDriveIntegrable
extend ActiveSupport::Concern
def google_drive_default_folder_name
name
end
def google_drive_default_resource_folder_name_fields
[]
end
def google_drive_default_additional_settings
{}
end
def google_drive_naming_fields
raise NotImplementedError
end
def google_drive_additional_settings_data
{}
end
def google_drive_redirect_url(query)
raise NotImplementedError
end
end
方法责任:
| 方法 | 需求 | 目的 |
|---|---|---|
google_drive_naming_fields | 对 | 资源文件夹名构建器中可用的字段 |
google_drive_redirect_url | 对 | OAuth 之后使用的绝对产品 URL |
google_drive_default_folder_name | 没有 | 初始主文件夹名; 默认为 name |
google_drive_default_resource_folder_name_fields | 没有 | 最初选中的命名域ID |
google_drive_default_additional_settings | 没有 | 默认主机拥有的设置散列; 默认为 {} |
google_drive_additional_settings_data | 没有 | 主机设置的元数据 UI; 默认为 {} |
命名字段有此形状:
{ id: "customer_name", label: "Customer name", kind: "text" }
仅 id 财务报告和审定财务报表 label 前端则需要.
产品专用选项(附录、PDF、注释等)属于
additional_settings启用 google_drive_additional_settings_data 发送 UI
元数据,例如可用的附件字段 renderAdditionalSettings。 。 。 .
4. 范围并授权发动机控制器
发动机故意将产品授权留给主机.
覆盖 API 控制器 :
# app/overrides/controllers/neeto_google_drive_engine/api/v1/base_controller_override.rb
NeetoGoogleDriveEngine::Api::V1::BaseController.class_eval do
private
def load_owner
@owner = current_user.organization.forms.find(params[:owner_id])
end
def authorize_google_drive!
authorize(:form_setting, :can_manage_integrations?)
end
end
单独配置 OAuth 拥有者查询, 因为 OAuth 回调不使用 常规认证的 API 会话 :
NeetoGoogleDriveEngine.oauth_owner_resolver = lambda do |user, owner_id|
user.organization.forms.find(owner_id)
end
永远不要使用未瞄准镜 Form.find, (中文(简体) ), Meeting.find,或者在
控制器覆盖或 OAuth 解析器 .
5. 安装并制造前端
安装软件包 :
yarn add @bigbinary/neeto-google-drive-frontend
显示连接屏幕 :
import { GoogleDriveIntegration } from "@bigbinary/neeto-google-drive-frontend";
const GoogleDriveConnection = ({ canManageIntegrations, formId }) => (
<GoogleDriveIntegration
canManageIntegrations={canManageIntegrations}
helpUrl="https://help.neetoform.com/google-drive"
managePath={`/admin/form/${formId}/settings/integrations/google-drive/manage`}
ownerId={formId}
resourceName="submission"
resourceNamePlural="submissions"
/>
);
显示设置屏幕 :
import { GoogleDriveSettings } from "@bigbinary/neeto-google-drive-frontend";
const GoogleDriveManage = ({
canManageIntegrations,
formId,
navigateToIntegration,
}) => (
<GoogleDriveSettings
canManageIntegrations={canManageIntegrations}
ownerId={formId}
resourceName="submission"
renderAdditionalSettings={FormGoogleDriveAdditionalSettings}
renderMissingIntegration={() => <MissingIntegration />}
onCancel={navigateToIntegration}
onSave={navigateToIntegration}
/>
);
GoogleDriveIntegration 需求 resourceName 财务报告和审定财务报表 resourceNamePlural。 。 。 .
GoogleDriveSettings 仅需要 resourceName。通过 "booking" 编号 :
"bookings" 一体化和一体化 "booking" 以 netoCal 形式设置.
renderAdditionalSettings 是可选的。 只有在产品需要的时候才能通过
额外选项。 组件接收Formik道具加 additionalSettingsData
从主人。 读取并写入主机值 values.additionalSettings
(例如 saveResourcePdf 或者说 selectedAttachmentFieldIds) (中文(简体) ).
不需要额外选项的产品, 如第一个 neetoCal 版本, 省略
renderAdditionalSettings 并只获取共享文件夹设置.
主机必须装入纳米 neetoGoogleDrive 翻译为其i18next
资源.
后端 API
装机暴露:
| 方法 | 终点 | 目的 |
|---|---|---|
GET | /neeto_google_drive/api/v1/google_drive/:owner_id | 连接状态、 授权 URL、 设置和字段 |
PATCH | /neeto_google_drive/api/v1/google_drive/:owner_id | 更新设置并重命名连接的主文件夹 |
DELETE | /neeto_google_drive/api/v1/google_drive/:owner_id | 不删除驱动器文件而断开连接 |
GET | /neeto_google_drive/api/v1/oauth | 谷歌 OAuth 回调 |
这个 GET 答复中包含:
{
"authorizationUrl": "https://accounts.google.com/...",
"connected": true,
"connectedEmail": "[email protected]",
"folderUrl": "https://drive.google.com/drive/folders/...",
"error": "",
"settings": {
"folderName": "Customer feedback",
"createResourceFolder": true,
"resourceFolderNameFields": ["full_name", "submission_date"],
"additionalSettings": {
"selectedAttachmentFieldIds": ["resume"],
"saveResourcePdf": true
}
},
"namingFields": [],
"additionalSettingsData": {
"attachmentFields": [{ "id": "resume", "label": "Resume" }]
}
}
additionalSettings 财务报告和审定财务报表 additionalSettingsData 是主机拥有的。 纳米
在不解释其密钥的情况下,将其固定并返回.
准确的 JSON 密钥外壳遵循主机应用程序的响应 转变.
引擎拥有的数据
NeetoGoogleDriveEngine::GoogleDrive 存储 :
- 多态
owner。 。 。 . - 活动或不活动状态.
- 连接了Google账户UID和电子邮件.
- 主文件夹ID和名.
- 导出设置和最新授权错误 .
NeetoGoogleDriveEngine::Credential 存储 :
- 加密访问令牌.
- 加密刷新令牌 .
- 托肯过期时间.
- 与整合的一对一链接.
google_drive.export_configuration 返回 JSON 安全设置快照
背景任务 :
{
"create_resource_folder" => true,
"resource_folder_name_fields" => ["full_name", "submission_date"],
"additional_settings" => {
"selected_attachment_field_ids" => ["resume"],
"save_resource_pdf" => true
}
}
主机任务从 additional_settings。通过这个
快照和当前工作的证书ID. 证书身份让一个
在所有者断开或更改账户后, stale 队列已停止工作 .
使用主机出口商的 Drive 客户端
主机出口商可以使用纳米自有客户端: 1
client = NeetoGoogleDriveEngine::Client.new(
owner.google_drive.credential
)
可用的操作 :
client.get_file(file_id)
client.create_folder(
name: folder_name,
parent_id: parent_id,
app_properties: metadata
)
client.rename_file(file_id, name)
client.delete_file(file_id)
client.list_files(
parent_id: parent_id,
mime_type: mime_type,
app_properties: metadata
)
client.upload_file(
name: filename,
parent_id: parent_id,
upload_source: file.path,
content_type: content_type,
app_properties: metadata
)
通过结构化过滤器到 list_files数字 :
files = client.list_files(
parent_id: target_folder.id,
mime_type: NeetoGoogleDriveEngine::Client::FOLDER_MIME_TYPE,
app_properties: {
"neeto_google_drive_resource" => "submission_folder",
"neeto_form_submission_id" => submission.id
}
)
在主机中不要构建 Google Drive 查询字符串 。 客户逃跑了 值和拥有 Google 特定查询语法.
使用纳米自有元数据键进行产品共享数据:
metadata = NeetoGoogleDriveEngine::FileMetadata
{
metadata::OWNER_ID_PROPERTY => owner.id,
metadata::OWNER_TYPE_PROPERTY => owner.class.name,
metadata::RESOURCE_PROPERTY => "main_folder"
}
保留特定产品的密钥,例如 neeto_form_submission_id,在主机中
产品.
需要表格实例: 已执行
需要格式的使用 Form 作为连接所有者 Submission 作为导出
资源.
模型适配器
module GoogleDriveIntegrable
extend ActiveSupport::Concern
SUBMISSION_DATE_FIELD = "submission_date"
SUBMISSION_ID_FIELD = "submission_id"
ATTACHMENT_RECORD_KINDS =
%w[file_upload take_photo digital_signature].freeze
def google_drive_default_folder_name
title
end
def google_drive_default_resource_folder_name_fields
naming_records = answer_records.where.not(kind: ATTACHMENT_RECORD_KINDS)
default_record =
naming_records.find_by(kind: :full_name) || naming_records.first
[default_record&.id&.to_s, SUBMISSION_DATE_FIELD].compact
end
def google_drive_naming_fields
answer_records
.where.not(kind: ATTACHMENT_RECORD_KINDS)
.map do |record|
{
id: record.id.to_s,
label: record.formatted_label,
kind: record.kind
}
end
.concat(
[
{
id: SUBMISSION_DATE_FIELD,
label: "Submission date",
kind: "system"
},
{
id: SUBMISSION_ID_FIELD,
label: "Submission ID",
kind: "system"
}
]
)
end
def google_drive_default_additional_settings
{
selected_attachment_field_ids: google_drive_attachment_fields.pluck(:id),
save_resource_pdf: true
}
end
def google_drive_additional_settings_data
{ attachment_fields: google_drive_attachment_fields }
end
def google_drive_attachment_fields
answer_records
.where(kind: ATTACHMENT_RECORD_KINDS)
.map { |record| { id: record.id.to_s, label: record.formatted_label } }
end
def google_drive_redirect_url(query)
url = URI(google_drive_integration_page_url)
url.query = query.to_query
url.to_s
end
end
附件字段和 PDF 切换为窗体所有 。 纳米只储存它们
内部 additional_settings 并返回附件字段元数据
google_drive_additional_settings_data 用于表单 renderAdditionalSettings
乌伊 UI.
背景导出
需要Form 描述提交完成时的证书和设置 :
if form.google_drive_exportable?
google_drive = form.google_drive
Integrations::GoogleDrive::SubmissionExporterJob.perform_async(
submission.id,
google_drive.credential.id,
google_drive.export_configuration
)
end
主机任务拥有 :
- 提交完成检查.
- 活动Storage附件搜索 .
- 提交文件夹命名 .
- 地方意识PDF生成.
- 重试调度和产品记录.
netoCal 实例:计划
这是基于 neetoCal 的当前模式和路线而提出的适配器 公约。 它尚未在neetoCal实施.
使用 Meeting 作为连接所有者 Booking 作为导出的资源。 执行
不将连接附加到每个 Booking。 。 。 .
计划初始化剂
NeetoGoogleDriveEngine.application_name = "NeetoCal"
NeetoGoogleDriveEngine.oauth_app_url =
Rails.application.vault.google_drive[:oauth_app_url]
NeetoGoogleDriveEngine.oauth_client_id =
Rails.application.vault.google_drive[:client_id]
NeetoGoogleDriveEngine.oauth_client_secret =
Rails.application.vault.google_drive[:client_secret]
NeetoGoogleDriveEngine.oauth_state_private_key =
Rails.application.vault.jwt[:connect_private_key]
NeetoGoogleDriveEngine.oauth_state_public_key =
Rails.application.vault.jwt[:connect_public_key]
NeetoGoogleDriveEngine.user_class = "User"
计划模型适配器
module Meetings::GoogleDriveConcern
extend ActiveSupport::Concern
BOOKER_NAME_FIELD = "booker_name"
BOOKING_DATE_FIELD = "booking_date"
BOOKING_ID_FIELD = "booking_id"
included do
include NeetoGoogleDriveEngine::HasGoogleDrive
end
def google_drive_default_folder_name
name
end
def google_drive_default_resource_folder_name_fields
[BOOKER_NAME_FIELD, BOOKING_DATE_FIELD]
end
def google_drive_naming_fields
[
{ id: BOOKER_NAME_FIELD, label: "Booker name", kind: "system" },
{ id: BOOKING_DATE_FIELD, label: "Booking date", kind: "system" },
{ id: BOOKING_ID_FIELD, label: "Booking ID", kind: "system" }
]
end
def google_drive_redirect_url(query)
url = URI(
"#{organization.root_url}/admin/scheduling-links/" \
"#{sid}/settings/integrations/google-drive"
)
url.query = query.to_query
url.to_s
end
end
计划控制器配置
使用 neetoCal 的组织范围界定和现有的 Google 工作表权限直至 引入 Google Drive 专用权限 :
NeetoGoogleDriveEngine::Api::V1::BaseController.class_eval do
private
def load_owner
@owner = @organization.meetings.find(params[:owner_id])
end
def authorize_google_drive!
authorize(Meeting, :can_manage_google_sheet?)
end
end
NeetoGoogleDriveEngine.oauth_owner_resolver = lambda do |user, owner_id|
user.organization.meetings.find(owner_id)
end
计划中的前端适配器
import {
GoogleDriveIntegration,
GoogleDriveSettings,
} from "@bigbinary/neeto-google-drive-frontend";
const BookingDriveConnection = ({
canManageGoogleSheet,
managePath,
meetingId,
}) => (
<GoogleDriveIntegration
canManageIntegrations={canManageGoogleSheet}
managePath={managePath}
ownerId={meetingId}
resourceName="booking"
resourceNamePlural="bookings"
/>
);
const BookingDriveSettings = ({
canManageGoogleSheet,
meetingId,
navigateToIntegration,
}) => (
<GoogleDriveSettings
canManageIntegrations={canManageGoogleSheet}
ownerId={meetingId}
resourceName="booking"
onCancel={navigateToIntegration}
onSave={navigateToIntegration}
/>
);
奥米 renderAdditionalSettings 直到卡尔需要特定产品选项.
连接副本的使用 resourceName 编号 : resourceNamePlural; 使用设置标签
resourceName。 。 。 .
当netoCal添加预订文件或预订 PDF 时,将这些控件变成
卡尔自有 renderAdditionalSettings 组件,将其值存储于
additional_settings,并在 neetoCal 中执行导出逻辑.
前端道具
GoogleDriveIntegration
| Prop | 需求 | 目的 |
|---|---|---|
ownerId | 对 | 拥有集成模式的标识 |
canManageIntegrations | 对 | 启用授权用户的集成查询 |
managePath | 对 | 管理 Google 驱动器打开的主机路由 |
resourceName | 对 | Singula UI 名词 |
resourceNamePlural | 对 | 多元 UI 名词 |
helpUrl | 没有 | 学习更多链接 |
GoogleDriveSettings
| Prop | 需求 | 目的 |
|---|---|---|
ownerId | 对 | 拥有集成模式的标识 |
canManageIntegrations | 对 | 启用授权用户的设置查询 |
onCancel | 对 | 取消后主机导航 |
onSave | 没有 | 成功保存后的主机导航 |
renderMissingIntegration | 没有 | 当所有者断开时主机用户界面 |
renderAdditionalSettings | 没有 | 特定产品备选方案的东道方部分 |
resourceName | 对 | 设置标签中使用的 Singular 名词 |
useGoogleDrive
当主机只需要连接状态时使用钩子 :
const { isConnected, isLoading } = useGoogleDrive({
ownerId: meetingId,
canManageIntegrations: canManageGoogleSheet,
});
它还返回更新、断开、加载和断开对话框状态.
连接和文件夹行为
连接
- UI获取一个签名的Google授权 URL.
- Google重定向到引擎 OAuth 回调.
- 召回验证状态、范围、用户和租户范围所有人.
- 引擎存储加密的证书 该业主.
- 引擎创建或恢复所有者主 Drive 文件夹.
重新连接
- 重新连接同一账户会保存设置和主文件夹ID.
- 一个被省略的刷新令牌只用于同一Google账户UID.
- 连接另一个账户可以清除旧文件夹 ID .
- 新账号收到自己的主文件夹.
断开连接
- 加密证书被销毁 .
- 这种整合变得不活跃.
- 设置和文件夹元数据仍被存储 .
- 已存在的 Google Drive 文件夹和文件不会被删除 .
- 未发送再连接邮件 .
处理错误
纳米暴露了可重复使用的错误类别:
NeetoGoogleDriveEngine::AUTHORIZATION_ERRORS
NeetoGoogleDriveEngine::TRANSIENT_ERRORS
NeetoGoogleDriveEngine::EXPORT_MAX_ATTEMPTS
主持人可以这样使用:
def perform
export_resource!
rescue *NeetoGoogleDriveEngine::TRANSIENT_ERRORS => exception
raise TransientError.new(exception)
rescue *NeetoGoogleDriveEngine::AUTHORIZATION_ERRORS
NeetoGoogleDriveEngine::DisconnectService.new(
google_drive,
error: "authorization_expired"
).process!
end
纳米存储连接UI的错误. 用户通知渠道 仍为东道主所有.
测试
运行后端套件 :
bundle exec rails test
构建前端软件包 :
yarn build
后端套房覆盖:
- 整合与认证模型.
- OAuth state and sorders exchange. (原始内容存档于2013-10-12). OAuth state and series exchange.
- 同一账户和不同账户重新连接.
- 驱动查询逃出并刷新令牌 .
- 主文件夹的创建,恢复,重命名,和货币行为.
东道产品应分别测试其授权的超标、出口工作, 资源内容、附件和PDF.
地方前端发展
跟着 前端软件包开发指南 用于在当地建立和消费软件包.
出版
跟着 构建和释放软件包 向导.