role.e2e-spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. /* tslint:disable:no-non-null-assertion */
  2. import { omit } from '@vendure/common/lib/omit';
  3. import {
  4. CUSTOMER_ROLE_CODE,
  5. DEFAULT_CHANNEL_CODE,
  6. SUPER_ADMIN_ROLE_CODE,
  7. } from '@vendure/common/lib/shared-constants';
  8. import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing';
  9. import gql from 'graphql-tag';
  10. import path from 'path';
  11. import { initialData } from '../../../e2e-common/e2e-initial-data';
  12. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  13. import { ROLE_FRAGMENT } from './graphql/fragments';
  14. import * as Codegen from './graphql/generated-e2e-admin-types';
  15. import { CurrencyCode, DeletionResult, LanguageCode, Permission } from './graphql/generated-e2e-admin-types';
  16. import { CREATE_ADMINISTRATOR, CREATE_CHANNEL, CREATE_ROLE, GET_CHANNELS, UPDATE_ADMINISTRATOR, UPDATE_ROLE } from './graphql/shared-definitions';
  17. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  18. import { sortById } from './utils/test-order-utils';
  19. describe('Role resolver', () => {
  20. const { server, adminClient } = createTestEnvironment(testConfig());
  21. let createdRole: Codegen.RoleFragment;
  22. let defaultRoles: Codegen.RoleFragment[];
  23. beforeAll(async () => {
  24. await server.init({
  25. initialData,
  26. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  27. customerCount: 1,
  28. });
  29. await adminClient.asSuperAdmin();
  30. }, TEST_SETUP_TIMEOUT_MS);
  31. afterAll(async () => {
  32. await server.destroy();
  33. });
  34. it('roles', async () => {
  35. const result = await adminClient.query<Codegen.GetRolesQuery, Codegen.GetRolesQueryVariables>(
  36. GET_ROLES,
  37. );
  38. defaultRoles = result.roles.items;
  39. expect(result.roles.items.length).toBe(2);
  40. expect(result.roles.totalItems).toBe(2);
  41. });
  42. it('createRole with invalid permission', async () => {
  43. try {
  44. await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(
  45. CREATE_ROLE,
  46. {
  47. input: {
  48. code: 'test',
  49. description: 'test role',
  50. permissions: ['ReadCatalogx' as any],
  51. },
  52. },
  53. );
  54. fail('Should have thrown');
  55. } catch (e: any) {
  56. expect(e.response.errors[0]?.extensions.code).toBe('BAD_USER_INPUT');
  57. }
  58. });
  59. it('createRole with no permissions includes Authenticated', async () => {
  60. const { createRole } = await adminClient.query<
  61. Codegen.CreateRoleMutation,
  62. Codegen.CreateRoleMutationVariables
  63. >(CREATE_ROLE, {
  64. input: {
  65. code: 'test',
  66. description: 'test role',
  67. permissions: [],
  68. },
  69. });
  70. expect(omit(createRole, ['channels'])).toEqual({
  71. code: 'test',
  72. description: 'test role',
  73. id: 'T_3',
  74. permissions: [Permission.Authenticated],
  75. });
  76. });
  77. it('createRole deduplicates permissions', async () => {
  78. const { createRole } = await adminClient.query<
  79. Codegen.CreateRoleMutation,
  80. Codegen.CreateRoleMutationVariables
  81. >(CREATE_ROLE, {
  82. input: {
  83. code: 'test2',
  84. description: 'test role2',
  85. permissions: [Permission.ReadSettings, Permission.ReadSettings],
  86. },
  87. });
  88. expect(omit(createRole, ['channels'])).toEqual({
  89. code: 'test2',
  90. description: 'test role2',
  91. id: 'T_4',
  92. permissions: [Permission.Authenticated, Permission.ReadSettings],
  93. });
  94. });
  95. it('createRole with permissions', async () => {
  96. const result = await adminClient.query<
  97. Codegen.CreateRoleMutation,
  98. Codegen.CreateRoleMutationVariables
  99. >(CREATE_ROLE, {
  100. input: {
  101. code: 'test',
  102. description: 'test role',
  103. permissions: [Permission.ReadCustomer, Permission.UpdateCustomer],
  104. },
  105. });
  106. createdRole = result.createRole;
  107. expect(createdRole).toEqual({
  108. code: 'test',
  109. description: 'test role',
  110. id: 'T_5',
  111. permissions: [Permission.Authenticated, Permission.ReadCustomer, Permission.UpdateCustomer],
  112. channels: [
  113. {
  114. code: DEFAULT_CHANNEL_CODE,
  115. id: 'T_1',
  116. token: 'e2e-default-channel',
  117. },
  118. ],
  119. });
  120. });
  121. it('role', async () => {
  122. const result = await adminClient.query<Codegen.GetRoleQuery, Codegen.GetRoleQueryVariables>(
  123. GET_ROLE,
  124. {
  125. id: createdRole.id,
  126. },
  127. );
  128. expect(result.role).toEqual(createdRole);
  129. });
  130. describe('updateRole', () => {
  131. it('updates role', async () => {
  132. const result = await adminClient.query<
  133. Codegen.UpdateRoleMutation,
  134. Codegen.UpdateRoleMutationVariables
  135. >(UPDATE_ROLE, {
  136. input: {
  137. id: createdRole.id,
  138. code: 'test-modified',
  139. description: 'test role modified',
  140. permissions: [
  141. Permission.ReadCustomer,
  142. Permission.UpdateCustomer,
  143. Permission.DeleteCustomer,
  144. ],
  145. },
  146. });
  147. expect(omit(result.updateRole, ['channels'])).toEqual({
  148. code: 'test-modified',
  149. description: 'test role modified',
  150. id: 'T_5',
  151. permissions: [
  152. Permission.Authenticated,
  153. Permission.ReadCustomer,
  154. Permission.UpdateCustomer,
  155. Permission.DeleteCustomer,
  156. ],
  157. });
  158. });
  159. it('works with partial input', async () => {
  160. const result = await adminClient.query<
  161. Codegen.UpdateRoleMutation,
  162. Codegen.UpdateRoleMutationVariables
  163. >(UPDATE_ROLE, {
  164. input: {
  165. id: createdRole.id,
  166. code: 'test-modified-again',
  167. },
  168. });
  169. expect(result.updateRole.code).toBe('test-modified-again');
  170. expect(result.updateRole.description).toBe('test role modified');
  171. expect(result.updateRole.permissions).toEqual([
  172. Permission.Authenticated,
  173. Permission.ReadCustomer,
  174. Permission.UpdateCustomer,
  175. Permission.DeleteCustomer,
  176. ]);
  177. });
  178. it('deduplicates permissions', async () => {
  179. const result = await adminClient.query<
  180. Codegen.UpdateRoleMutation,
  181. Codegen.UpdateRoleMutationVariables
  182. >(UPDATE_ROLE, {
  183. input: {
  184. id: createdRole.id,
  185. permissions: [
  186. Permission.Authenticated,
  187. Permission.Authenticated,
  188. Permission.ReadCustomer,
  189. Permission.ReadCustomer,
  190. ],
  191. },
  192. });
  193. expect(result.updateRole.permissions).toEqual([
  194. Permission.Authenticated,
  195. Permission.ReadCustomer,
  196. ]);
  197. });
  198. it(
  199. 'does not allow setting non-assignable permissions - Owner',
  200. assertThrowsWithMessage(async () => {
  201. await adminClient.query<Codegen.UpdateRoleMutation, Codegen.UpdateRoleMutationVariables>(
  202. UPDATE_ROLE,
  203. {
  204. input: {
  205. id: createdRole.id,
  206. permissions: [Permission.Owner],
  207. },
  208. },
  209. );
  210. }, 'The permission "Owner" may not be assigned'),
  211. );
  212. it(
  213. 'does not allow setting non-assignable permissions - Public',
  214. assertThrowsWithMessage(async () => {
  215. await adminClient.query<Codegen.UpdateRoleMutation, Codegen.UpdateRoleMutationVariables>(
  216. UPDATE_ROLE,
  217. {
  218. input: {
  219. id: createdRole.id,
  220. permissions: [Permission.Public],
  221. },
  222. },
  223. );
  224. }, 'The permission "Public" may not be assigned'),
  225. );
  226. it(
  227. 'does not allow setting SuperAdmin permission',
  228. assertThrowsWithMessage(async () => {
  229. await adminClient.query<Codegen.UpdateRoleMutation, Codegen.UpdateRoleMutationVariables>(
  230. UPDATE_ROLE,
  231. {
  232. input: {
  233. id: createdRole.id,
  234. permissions: [Permission.SuperAdmin],
  235. },
  236. },
  237. );
  238. }, 'The permission "SuperAdmin" may not be assigned'),
  239. );
  240. it(
  241. 'is not allowed for SuperAdmin role',
  242. assertThrowsWithMessage(async () => {
  243. const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE);
  244. if (!superAdminRole) {
  245. fail(`Could not find SuperAdmin role`);
  246. return;
  247. }
  248. return adminClient.query<Codegen.UpdateRoleMutation, Codegen.UpdateRoleMutationVariables>(
  249. UPDATE_ROLE,
  250. {
  251. input: {
  252. id: superAdminRole.id,
  253. code: 'superadmin-modified',
  254. description: 'superadmin modified',
  255. permissions: [Permission.Authenticated],
  256. },
  257. },
  258. );
  259. }, `The role '${SUPER_ADMIN_ROLE_CODE}' cannot be modified`),
  260. );
  261. it(
  262. 'is not allowed for Customer role',
  263. assertThrowsWithMessage(async () => {
  264. const customerRole = defaultRoles.find(r => r.code === CUSTOMER_ROLE_CODE);
  265. if (!customerRole) {
  266. fail(`Could not find Customer role`);
  267. return;
  268. }
  269. return adminClient.query<Codegen.UpdateRoleMutation, Codegen.UpdateRoleMutationVariables>(
  270. UPDATE_ROLE,
  271. {
  272. input: {
  273. id: customerRole.id,
  274. code: 'customer-modified',
  275. description: 'customer modified',
  276. permissions: [Permission.Authenticated, Permission.DeleteAdministrator],
  277. },
  278. },
  279. );
  280. }, `The role '${CUSTOMER_ROLE_CODE}' cannot be modified`),
  281. );
  282. });
  283. it(
  284. 'deleteRole is not allowed for Customer role',
  285. assertThrowsWithMessage(async () => {
  286. const customerRole = defaultRoles.find(r => r.code === CUSTOMER_ROLE_CODE);
  287. if (!customerRole) {
  288. fail(`Could not find Customer role`);
  289. return;
  290. }
  291. return adminClient.query<Codegen.DeleteRoleMutation, Codegen.DeleteRoleMutationVariables>(
  292. DELETE_ROLE,
  293. {
  294. id: customerRole.id,
  295. },
  296. );
  297. }, `The role '${CUSTOMER_ROLE_CODE}' cannot be deleted`),
  298. );
  299. it(
  300. 'deleteRole is not allowed for SuperAdmin role',
  301. assertThrowsWithMessage(async () => {
  302. const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE);
  303. if (!superAdminRole) {
  304. fail(`Could not find Customer role`);
  305. return;
  306. }
  307. return adminClient.query<Codegen.DeleteRoleMutation, Codegen.DeleteRoleMutationVariables>(
  308. DELETE_ROLE,
  309. {
  310. id: superAdminRole.id,
  311. },
  312. );
  313. }, `The role '${SUPER_ADMIN_ROLE_CODE}' cannot be deleted`),
  314. );
  315. it('deleteRole deletes a role', async () => {
  316. const { deleteRole } = await adminClient.query<
  317. Codegen.DeleteRoleMutation,
  318. Codegen.DeleteRoleMutationVariables
  319. >(DELETE_ROLE, {
  320. id: createdRole.id,
  321. });
  322. expect(deleteRole.result).toBe(DeletionResult.DELETED);
  323. const { role } = await adminClient.query<Codegen.GetRoleQuery, Codegen.GetRoleQueryVariables>(
  324. GET_ROLE,
  325. {
  326. id: createdRole.id,
  327. },
  328. );
  329. expect(role).toBeNull();
  330. });
  331. describe('multi-channel', () => {
  332. let secondChannel: Codegen.ChannelFragment;
  333. let multiChannelRole: Codegen.CreateRoleMutation['createRole'];
  334. beforeAll(async () => {
  335. const { createChannel } = await adminClient.query<
  336. Codegen.CreateChannelMutation,
  337. Codegen.CreateChannelMutationVariables
  338. >(CREATE_CHANNEL, {
  339. input: {
  340. code: 'second-channel',
  341. token: 'second-channel-token',
  342. defaultLanguageCode: LanguageCode.en,
  343. currencyCode: CurrencyCode.GBP,
  344. pricesIncludeTax: true,
  345. defaultShippingZoneId: 'T_1',
  346. defaultTaxZoneId: 'T_1',
  347. },
  348. });
  349. secondChannel = createChannel as any;
  350. });
  351. it('createRole with specified channel', async () => {
  352. const result = await adminClient.query<
  353. Codegen.CreateRoleMutation,
  354. Codegen.CreateRoleMutationVariables
  355. >(CREATE_ROLE, {
  356. input: {
  357. code: 'multi-test',
  358. description: 'multi channel test role',
  359. permissions: [Permission.ReadCustomer],
  360. channelIds: [secondChannel.id],
  361. },
  362. });
  363. multiChannelRole = result.createRole;
  364. expect(multiChannelRole).toEqual({
  365. code: 'multi-test',
  366. description: 'multi channel test role',
  367. id: 'T_6',
  368. permissions: [Permission.Authenticated, Permission.ReadCustomer],
  369. channels: [
  370. {
  371. code: 'second-channel',
  372. id: 'T_2',
  373. token: 'second-channel-token',
  374. },
  375. ],
  376. });
  377. });
  378. it('updateRole with specified channel', async () => {
  379. const { updateRole } = await adminClient.query<
  380. Codegen.UpdateRoleMutation,
  381. Codegen.UpdateRoleMutationVariables
  382. >(UPDATE_ROLE, {
  383. input: {
  384. id: multiChannelRole.id,
  385. channelIds: ['T_1', 'T_2'],
  386. },
  387. });
  388. expect(updateRole.channels.sort(sortById)).toEqual([
  389. {
  390. code: DEFAULT_CHANNEL_CODE,
  391. id: 'T_1',
  392. token: 'e2e-default-channel',
  393. },
  394. {
  395. code: 'second-channel',
  396. id: 'T_2',
  397. token: 'second-channel-token',
  398. },
  399. ]);
  400. });
  401. });
  402. // https://github.com/vendure-ecommerce/vendure/issues/1874
  403. describe('role escalation', () => {
  404. let defaultChannel: Codegen.GetChannelsQuery['channels'][number];
  405. let secondChannel: Codegen.GetChannelsQuery['channels'][number];
  406. let limitedAdmin: Codegen.CreateAdministratorMutation['createAdministrator'];
  407. let orderReaderRole: Codegen.CreateRoleMutation['createRole'];
  408. let adminCreatorRole: Codegen.CreateRoleMutation['createRole'];
  409. let adminCreatorAdministrator: Codegen.CreateAdministratorMutation['createAdministrator'];
  410. beforeAll(async () => {
  411. const { channels } = await adminClient.query<Codegen.GetChannelsQuery>(GET_CHANNELS);
  412. defaultChannel = channels.find(c => c.token === E2E_DEFAULT_CHANNEL_TOKEN)!;
  413. secondChannel = channels.find(c => c.token !== E2E_DEFAULT_CHANNEL_TOKEN)!;
  414. await adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  415. await adminClient.asSuperAdmin();
  416. const { createRole } = await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(
  417. CREATE_ROLE,
  418. {
  419. input: {
  420. code: 'second-channel-admin-manager',
  421. description: '',
  422. channelIds: [secondChannel.id],
  423. permissions: [
  424. Permission.CreateAdministrator,
  425. Permission.ReadAdministrator,
  426. Permission.UpdateAdministrator,
  427. Permission.DeleteAdministrator,
  428. ],
  429. },
  430. },
  431. );
  432. const { createAdministrator } = await adminClient.query<
  433. Codegen.CreateAdministratorMutation,
  434. Codegen.CreateAdministratorMutationVariables
  435. >(CREATE_ADMINISTRATOR, {
  436. input: {
  437. firstName: 'channel2',
  438. lastName: 'admin manager',
  439. emailAddress: 'channel2@test.com',
  440. roleIds: [createRole.id],
  441. password: 'test',
  442. },
  443. });
  444. limitedAdmin = createAdministrator;
  445. const { createRole: createRole2 } = await adminClient.query<
  446. Codegen.CreateRoleMutation,
  447. Codegen.CreateRoleMutationVariables
  448. >(CREATE_ROLE, {
  449. input: {
  450. code: 'second-channel-order-manager',
  451. description: '',
  452. channelIds: [secondChannel.id],
  453. permissions: [Permission.ReadOrder],
  454. },
  455. });
  456. orderReaderRole = createRole2;
  457. adminClient.setChannelToken(secondChannel.token);
  458. await adminClient.asUserWithCredentials(limitedAdmin.emailAddress, 'test');
  459. });
  460. it(
  461. 'limited admin cannot create Role with SuperAdmin permission',
  462. assertThrowsWithMessage(async () => {
  463. await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(CREATE_ROLE, {
  464. input: {
  465. code: 'evil-superadmin',
  466. description: '',
  467. channelIds: [secondChannel.id],
  468. permissions: [Permission.SuperAdmin],
  469. },
  470. });
  471. }, 'The permission "SuperAdmin" may not be assigned'),
  472. );
  473. it(
  474. 'limited admin cannot create Administrator with SuperAdmin role',
  475. assertThrowsWithMessage(async () => {
  476. const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE)!;
  477. await adminClient.query<Codegen.CreateAdministratorMutation, Codegen.CreateAdministratorMutationVariables>(
  478. CREATE_ADMINISTRATOR,
  479. {
  480. input: {
  481. firstName: 'Dr',
  482. lastName: 'Evil',
  483. emailAddress: 'drevil@test.com',
  484. roleIds: [superAdminRole.id],
  485. password: 'test',
  486. },
  487. },
  488. );
  489. }, 'Active user does not have sufficient permissions'),
  490. );
  491. it(
  492. 'limited admin cannot create Role with permissions it itself does not have',
  493. assertThrowsWithMessage(async () => {
  494. await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(CREATE_ROLE, {
  495. input: {
  496. code: 'evil-order-manager',
  497. description: '',
  498. channelIds: [secondChannel.id],
  499. permissions: [Permission.ReadOrder],
  500. },
  501. });
  502. }, 'Active user does not have sufficient permissions'),
  503. );
  504. it(
  505. 'limited admin cannot create Role on channel it does not have permissions on',
  506. assertThrowsWithMessage(async () => {
  507. await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(CREATE_ROLE, {
  508. input: {
  509. code: 'evil-order-manager',
  510. description: '',
  511. channelIds: [defaultChannel.id],
  512. permissions: [Permission.CreateAdministrator],
  513. },
  514. });
  515. }, 'You are not currently authorized to perform this action'),
  516. );
  517. it(
  518. 'limited admin cannot create Administrator with a Role with greater permissions than they themselves have',
  519. assertThrowsWithMessage(async () => {
  520. await adminClient.query<Codegen.CreateAdministratorMutation, Codegen.CreateAdministratorMutationVariables>(
  521. CREATE_ADMINISTRATOR,
  522. {
  523. input: {
  524. firstName: 'Dr',
  525. lastName: 'Evil',
  526. emailAddress: 'drevil@test.com',
  527. roleIds: [orderReaderRole.id],
  528. password: 'test',
  529. },
  530. },
  531. );
  532. }, 'Active user does not have sufficient permissions'),
  533. );
  534. it('limited admin can create Role with permissions it itself has', async () => {
  535. const { createRole } = await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(
  536. CREATE_ROLE,
  537. {
  538. input: {
  539. code: 'good-admin-creator',
  540. description: '',
  541. channelIds: [secondChannel.id],
  542. permissions: [Permission.CreateAdministrator],
  543. },
  544. },
  545. );
  546. expect(createRole.code).toBe('good-admin-creator');
  547. adminCreatorRole = createRole;
  548. });
  549. it('limited admin can create Administrator with permissions it itself has', async () => {
  550. const { createAdministrator } = await adminClient.query<
  551. Codegen.CreateAdministratorMutation,
  552. Codegen.CreateAdministratorMutationVariables
  553. >(CREATE_ADMINISTRATOR, {
  554. input: {
  555. firstName: 'Admin',
  556. lastName: 'Creator',
  557. emailAddress: 'admincreator@test.com',
  558. roleIds: [adminCreatorRole.id],
  559. password: 'test',
  560. },
  561. });
  562. expect(createAdministrator.emailAddress).toBe('admincreator@test.com');
  563. adminCreatorAdministrator = createAdministrator;
  564. });
  565. it(
  566. 'limited admin cannot update Role with permissions it itself lacks',
  567. assertThrowsWithMessage(async () => {
  568. await adminClient.query<Codegen.UpdateRoleMutation, Codegen.UpdateRoleMutationVariables>(UPDATE_ROLE, {
  569. input: {
  570. id: adminCreatorRole.id,
  571. permissions: [Permission.ReadOrder],
  572. },
  573. });
  574. }, 'Active user does not have sufficient permissions'),
  575. );
  576. it(
  577. 'limited admin cannot update Administrator with Role containing permissions it itself lacks',
  578. assertThrowsWithMessage(async () => {
  579. await adminClient.query<Codegen.UpdateAdministratorMutation, Codegen.UpdateAdministratorMutationVariables>(
  580. UPDATE_ADMINISTRATOR,
  581. {
  582. input: {
  583. id: adminCreatorAdministrator.id,
  584. roleIds: [adminCreatorRole.id, orderReaderRole.id],
  585. },
  586. },
  587. );
  588. }, 'Active user does not have sufficient permissions'),
  589. );
  590. });
  591. });
  592. export const GET_ROLES = gql`
  593. query GetRoles($options: RoleListOptions) {
  594. roles(options: $options) {
  595. items {
  596. ...Role
  597. }
  598. totalItems
  599. }
  600. }
  601. ${ROLE_FRAGMENT}
  602. `;
  603. export const GET_ROLE = gql`
  604. query GetRole($id: ID!) {
  605. role(id: $id) {
  606. ...Role
  607. }
  608. }
  609. ${ROLE_FRAGMENT}
  610. `;
  611. export const DELETE_ROLE = gql`
  612. mutation DeleteRole($id: ID!) {
  613. deleteRole(id: $id) {
  614. result
  615. message
  616. }
  617. }
  618. `;