IFTTT thông qua các chỉ thị
Gato GraphQL cung cấp khả năng triển khai các chiến lược IFTTT (If This Then That — Nếu Điều Này Thì Điều Đó) thông qua các chỉ thị. Các chỉ thị này được thêm động vào truy vấn bất cứ khi nào một trường hoặc chỉ thị cụ thể xuất hiện trong truy vấn.
Nói chung, IFTTT là các quy tắc kích hoạt hành động bất cứ khi nào một sự kiện được chỉ định xảy ra. Trong trường hợp của chúng ta, các cặp sự kiện/hành động là:
- Nếu "trường X được tìm thấy trong truy vấn" thì "gắn chỉ thị Y vào trường X"
- Nếu "chỉ thị Z được tìm thấy trong truy vấn" thì "thực thi chỉ thị Y trước/sau chỉ thị Z"
Việc thêm động các chỉ thị IFTTT vào schema là một quá trình đệ quy: bản thân chỉ thị đó có thể được cấu hình với tập hợp các chỉ thị IFTTT riêng của nó, và chúng cũng được thêm vào chuỗi chỉ thị.
Nó được sử dụng ở đâu
Phía sau hậu trường, các client trong Gato GraphQL sử dụng cơ chế này để cấu hình GraphQL schema.
Ví dụ, Access Control cho phép chúng ta chọn các quy tắc kiểm soát truy cập áp dụng cho các thao tác, trường và chỉ thị. Chính nhờ IFTTT mà các quy tắc này được áp dụng cho những phần tử đó trong GraphQL schema.

Nói chung, đây là một số trường hợp sử dụng:
Xác định max-age của cache control theo từng trường
Gắn chỉ thị @CacheControl vào tất cả các trường, tùy chỉnh giá trị của tham số maxAge: 1 năm cho trường url của Post, và 1 giờ cho trường title.
Thiết lập kiểm soát truy cập
Gắn chỉ thị @validateDoesLoggedInUserHaveAnyRole vào trường email của kiểu User, để chỉ những quản trị viên mới có thể truy vấn email người dùng.
Đồng bộ hóa kiểm soát truy cập với cache control
Bằng cách xâu chuỗi các chỉ thị, chúng ta có thể đảm bảo rằng bất cứ khi nào xác thực xem người dùng có thể truy cập một trường/chỉ thị hay không, phản hồi sẽ không được lưu vào cache. Ví dụ:
- Gắn chỉ thị
@validateIsUserLoggedInvào trườngme - Gắn chỉ thị
@CacheControlvới giá trị đối sốmaxAgelà0vào chỉ thị@validateIsUserLoggedIn.
Tăng cường bảo mật
Gắn chỉ thị @validateIsUserLoggedIn vào chỉ thị @translate, để tránh các tác nhân độc hại thực thi queries chống lại dịch vụ GraphQL có thể làm sập máy chủ và tăng vọt hóa đơn (trong trường hợp này, @translate dựa trên Google Translate và phải trả phí khi sử dụng dịch vụ này)
Cách thức hoạt động
Làm thế nào để thêm các chỉ thị vào schema thông qua IFTTT? Ví dụ, giả sử chúng ta muốn tạo một chỉ thị tùy chỉnh @authorize(role: String!), để xác thực rằng người dùng thực thi trường myPosts có vai trò mong đợi là author, hoặc hiển thị lỗi nếu không.
Nếu chúng ta tạo schema bằng SDL, nó sẽ trông như thế này:
directive @authorize(role: String!) on FIELD_DEFINITION
type User {
myPosts: [Post] @authorize(role: "author")
}Quy tắc IFTTT xác định cùng một ý định mà SDL ở trên đang khai báo: bất cứ khi nào yêu cầu trường myPosts, thực thi chỉ thị @authorize(role: "author") trên nó. Sau đó, bất cứ khi nào trường myPosts được tìm thấy trong truy vấn, engine sẽ tự động gắn @authorize(role: 'author') vào trường đó trong truy vấn thực thi.
Các quy tắc IFTTT cũng có thể được kích hoạt khi gặp một chỉ thị, không chỉ một trường. Ví dụ, chúng ta có thể thiết lập quy tắc "Bất cứ khi nào chỉ thị @translate được tìm thấy trong truy vấn, thực thi chỉ thị @cache(time: 3600) trên trường đó".
Việc thêm các chỉ thị IFTTT vào truy vấn là một quá trình đệ quy: nó sẽ kích hoạt một sự kiện mới được xử lý bởi các quy tắc IFTTT, có khả năng gắn thêm các chỉ thị khác vào truy vấn, và cứ thế tiếp tục.
Ví dụ, quy tắc "Bất cứ khi nào chỉ thị @cache được tìm thấy, thực thi chỉ thị @log" sẽ ghi lại một mục về việc thực thi trường, sau đó kích hoạt một sự kiện mới liên quan đến chỉ thị vừa được thêm vào.
Thiết lập thông qua mã PHP
Kiểu User có các trường roles và capabilities, có thể được coi là thông tin nhạy cảm, do đó chúng không nên được truy cập bởi người dùng ngẫu nhiên.
Vì vậy chúng ta có thể gắn chỉ thị @validateDoesLoggedInUserHaveAnyRole vào hai trường này, được cấu hình để xác thực rằng chỉ người dùng có vai trò nhất định (được cấu hình thông qua biến môi trường) mới có thể truy cập chúng. Cấu hình được cung cấp thông qua một CompilerPass:
$accessControlManagerDefinition = $containerBuilderWrapper->getDefinition(AccessControlManagerInterface::class);
if ($roles = Environment::anyRoleLoggedInUserMustHaveToAccessRolesFields()) {
$accessControlManagerDefinition->addMethodCall(
'addEntriesForFields',
[
UserRolesAccessControlGroups::ROLES,
[
[RootObjectTypeResolver::class, 'roles', $roles],
[UserObjectTypeResolver::class, 'roles', $roles],
[RootObjectTypeResolver::class, 'capabilities', $roles],
[UserObjectTypeResolver::class, 'capabilities', $roles],
]
]
);
}
if ($capabilities = Environment::anyCapabilityLoggedInUserMustHaveToAccessRolesFields()) {
$accessControlManagerDefinition->addMethodCall(
'addEntriesForFields',
[
UserCapabilitiesAccessControlGroups::CAPABILITIES,
[
[RootObjectTypeResolver::class, 'roles', $capabilities],
[UserObjectTypeResolver::class, 'roles', $capabilities],
[RootObjectTypeResolver::class, 'capabilities', $capabilities],
[UserObjectTypeResolver::class, 'capabilities', $capabilities],
]
]
);
}Khi thực thi truy vấn, những người dùng chưa đăng nhập và người dùng không có các vai trò cần thiết sẽ không được phép truy cập các trường đó.