Add right requests
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
194
apps/console/src/hooks/graph/RightsRequestGraph.ts
Normal file
194
apps/console/src/hooks/graph/RightsRequestGraph.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutation } from "react-relay";
|
||||
import { useConfirm } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { promisifyMutation, sprintf } from "@probo/helpers";
|
||||
import { useMutationWithToasts } from "../useMutationWithToasts";
|
||||
|
||||
export const RightsRequestsConnectionKey = "RightsRequestsPage_rightsRequests";
|
||||
|
||||
export const rightsRequestsQuery = graphql`
|
||||
query RightsRequestGraphListQuery($organizationId: ID!) {
|
||||
node(id: $organizationId) {
|
||||
... on Organization {
|
||||
...RightsRequestsPageFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const rightsRequestNodeQuery = graphql`
|
||||
query RightsRequestGraphNodeQuery($rightsRequestId: ID!) {
|
||||
node(id: $rightsRequestId) {
|
||||
... on RightsRequest {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
contact
|
||||
details
|
||||
deadline
|
||||
actionTaken
|
||||
organization {
|
||||
id
|
||||
name
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const createRightsRequestMutation = graphql`
|
||||
mutation RightsRequestGraphCreateMutation(
|
||||
$input: CreateRightsRequestInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
contact
|
||||
details
|
||||
deadline
|
||||
actionTaken
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const updateRightsRequestMutation = graphql`
|
||||
mutation RightsRequestGraphUpdateMutation($input: UpdateRightsRequestInput!) {
|
||||
updateRightsRequest(input: $input) {
|
||||
rightsRequest {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
contact
|
||||
details
|
||||
deadline
|
||||
actionTaken
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const deleteRightsRequestMutation = graphql`
|
||||
mutation RightsRequestGraphDeleteMutation(
|
||||
$input: DeleteRightsRequestInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
deleteRightsRequest(input: $input) {
|
||||
deletedRightsRequestId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const useDeleteRightsRequest = (
|
||||
request: { id: string },
|
||||
connectionId: string
|
||||
) => {
|
||||
const { __ } = useTranslate();
|
||||
const [mutate] = useMutationWithToasts(deleteRightsRequestMutation, {
|
||||
successMessage: __("Rights request deleted successfully"),
|
||||
errorMessage: __("Failed to delete rights request"),
|
||||
});
|
||||
const confirm = useConfirm();
|
||||
|
||||
return () => {
|
||||
confirm(
|
||||
() =>
|
||||
mutate({
|
||||
variables: {
|
||||
input: {
|
||||
rightsRequestId: request.id,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
"This will permanently delete the rights request. This action cannot be undone."
|
||||
)
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const useCreateRightsRequest = (connectionId: string) => {
|
||||
const [mutate] = useMutation(createRightsRequestMutation);
|
||||
const { __ } = useTranslate();
|
||||
|
||||
return (input: {
|
||||
organizationId: string;
|
||||
requestType: string;
|
||||
requestState: string;
|
||||
dataSubject?: string;
|
||||
contact?: string;
|
||||
details?: string;
|
||||
deadline?: string;
|
||||
actionTaken?: string;
|
||||
}) => {
|
||||
if (!input.organizationId) {
|
||||
return alert(__("Failed to create rights request: organization is required"));
|
||||
}
|
||||
if (!input.requestType) {
|
||||
return alert(__("Failed to create rights request: request type is required"));
|
||||
}
|
||||
if (!input.requestState) {
|
||||
return alert(__("Failed to create rights request: request state is required"));
|
||||
}
|
||||
|
||||
return promisifyMutation(mutate)({
|
||||
variables: {
|
||||
input: {
|
||||
organizationId: input.organizationId,
|
||||
requestType: input.requestType,
|
||||
requestState: input.requestState,
|
||||
dataSubject: input.dataSubject,
|
||||
contact: input.contact,
|
||||
details: input.details,
|
||||
deadline: input.deadline,
|
||||
actionTaken: input.actionTaken,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const useUpdateRightsRequest = () => {
|
||||
const [mutate] = useMutation(updateRightsRequestMutation);
|
||||
const { __ } = useTranslate();
|
||||
|
||||
return (input: {
|
||||
id: string;
|
||||
requestType?: string;
|
||||
requestState?: string;
|
||||
dataSubject?: string;
|
||||
contact?: string;
|
||||
details?: string;
|
||||
deadline?: string | null;
|
||||
actionTaken?: string;
|
||||
}) => {
|
||||
if (!input.id) {
|
||||
return alert(__("Failed to update rights request: ID is required"));
|
||||
}
|
||||
|
||||
return promisifyMutation(mutate)({
|
||||
variables: {
|
||||
input,
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
231
apps/console/src/hooks/graph/__generated__/RightsRequestGraphCreateMutation.graphql.ts
generated
Normal file
231
apps/console/src/hooks/graph/__generated__/RightsRequestGraphCreateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* @generated SignedSource<<630d2ddf409889a6c1632063d18a2545>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RightsRequestState = "DONE" | "IN_PROGRESS" | "TODO";
|
||||
export type RightsRequestType = "ACCESS" | "DELETION" | "PORTABILITY";
|
||||
export type CreateRightsRequestInput = {
|
||||
actionTaken?: string | null | undefined;
|
||||
contact?: string | null | undefined;
|
||||
dataSubject?: string | null | undefined;
|
||||
deadline?: any | null | undefined;
|
||||
details?: string | null | undefined;
|
||||
organizationId: string;
|
||||
requestState: RightsRequestState;
|
||||
requestType: RightsRequestType;
|
||||
};
|
||||
export type RightsRequestGraphCreateMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateRightsRequestInput;
|
||||
};
|
||||
export type RightsRequestGraphCreateMutation$data = {
|
||||
readonly createRightsRequest: {
|
||||
readonly rightsRequestEdge: {
|
||||
readonly node: {
|
||||
readonly actionTaken: string | null | undefined;
|
||||
readonly contact: string | null | undefined;
|
||||
readonly createdAt: any;
|
||||
readonly dataSubject: string | null | undefined;
|
||||
readonly deadline: any | null | undefined;
|
||||
readonly details: string | null | undefined;
|
||||
readonly id: string;
|
||||
readonly requestState: RightsRequestState;
|
||||
readonly requestType: RightsRequestType;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type RightsRequestGraphCreateMutation = {
|
||||
response: RightsRequestGraphCreateMutation$data;
|
||||
variables: RightsRequestGraphCreateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequestEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "rightsRequestEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequest",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestState",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "dataSubject",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "contact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "details",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "actionTaken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RightsRequestGraphCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateRightsRequestPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createRightsRequest",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "RightsRequestGraphCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateRightsRequestPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createRightsRequest",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "rightsRequestEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "2642d9a5f58aa90190df6ccf73e6e06f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RightsRequestGraphCreateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation RightsRequestGraphCreateMutation(\n $input: CreateRightsRequestInput!\n) {\n createRightsRequest(input: $input) {\n rightsRequestEdge {\n node {\n id\n requestType\n requestState\n dataSubject\n contact\n details\n deadline\n actionTaken\n createdAt\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "98fa74d3219cf56655e74441af4dc01d";
|
||||
|
||||
export default node;
|
||||
132
apps/console/src/hooks/graph/__generated__/RightsRequestGraphDeleteMutation.graphql.ts
generated
Normal file
132
apps/console/src/hooks/graph/__generated__/RightsRequestGraphDeleteMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @generated SignedSource<<274e16758d468bf7fb911965acca3264>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeleteRightsRequestInput = {
|
||||
rightsRequestId: string;
|
||||
};
|
||||
export type RightsRequestGraphDeleteMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: DeleteRightsRequestInput;
|
||||
};
|
||||
export type RightsRequestGraphDeleteMutation$data = {
|
||||
readonly deleteRightsRequest: {
|
||||
readonly deletedRightsRequestId: string;
|
||||
};
|
||||
};
|
||||
export type RightsRequestGraphDeleteMutation = {
|
||||
response: RightsRequestGraphDeleteMutation$data;
|
||||
variables: RightsRequestGraphDeleteMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deletedRightsRequestId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RightsRequestGraphDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteRightsRequestPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteRightsRequest",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "RightsRequestGraphDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteRightsRequestPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteRightsRequest",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "deletedRightsRequestId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "c81b44380a4647a241c72fe1ca359246",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RightsRequestGraphDeleteMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation RightsRequestGraphDeleteMutation(\n $input: DeleteRightsRequestInput!\n) {\n deleteRightsRequest(input: $input) {\n deletedRightsRequestId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "e657b62ad808fa4fd5902cf4eea4ca3b";
|
||||
|
||||
export default node;
|
||||
295
apps/console/src/hooks/graph/__generated__/RightsRequestGraphListQuery.graphql.ts
generated
Normal file
295
apps/console/src/hooks/graph/__generated__/RightsRequestGraphListQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* @generated SignedSource<<22e08aea59fb3427d7770d89a44d6e9e>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type RightsRequestGraphListQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type RightsRequestGraphListQuery$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"RightsRequestsPageFragment">;
|
||||
};
|
||||
};
|
||||
export type RightsRequestGraphListQuery = {
|
||||
response: RightsRequestGraphListQuery$data;
|
||||
variables: RightsRequestGraphListQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 10
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RightsRequestGraphListQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "RightsRequestsPageFragment"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "RightsRequestGraphListQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "RightsRequestConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "rightsRequests",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalCount",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequestEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequest",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestState",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "dataSubject",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "contact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "details",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "actionTaken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": "rightsRequests(first:10)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "RightsRequestsPage_rightsRequests",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "rightsRequests"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "77f124136a2da2eb84fb134e5d10396b",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RightsRequestGraphListQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query RightsRequestGraphListQuery(\n $organizationId: ID!\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n ...RightsRequestsPageFragment\n }\n id\n }\n}\n\nfragment RightsRequestsPageFragment on Organization {\n id\n rightsRequests(first: 10) {\n totalCount\n edges {\n node {\n id\n requestType\n requestState\n dataSubject\n contact\n details\n deadline\n actionTaken\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "6ff83d9e0017a4fb245ad9f13106a439";
|
||||
|
||||
export default node;
|
||||
241
apps/console/src/hooks/graph/__generated__/RightsRequestGraphNodeQuery.graphql.ts
generated
Normal file
241
apps/console/src/hooks/graph/__generated__/RightsRequestGraphNodeQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* @generated SignedSource<<7314c67aea13775f772b81880c524a60>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RightsRequestState = "DONE" | "IN_PROGRESS" | "TODO";
|
||||
export type RightsRequestType = "ACCESS" | "DELETION" | "PORTABILITY";
|
||||
export type RightsRequestGraphNodeQuery$variables = {
|
||||
rightsRequestId: string;
|
||||
};
|
||||
export type RightsRequestGraphNodeQuery$data = {
|
||||
readonly node: {
|
||||
readonly actionTaken?: string | null | undefined;
|
||||
readonly contact?: string | null | undefined;
|
||||
readonly createdAt?: any;
|
||||
readonly dataSubject?: string | null | undefined;
|
||||
readonly deadline?: any | null | undefined;
|
||||
readonly details?: string | null | undefined;
|
||||
readonly id?: string;
|
||||
readonly organization?: {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
readonly requestState?: RightsRequestState;
|
||||
readonly requestType?: RightsRequestType;
|
||||
readonly updatedAt?: any;
|
||||
};
|
||||
};
|
||||
export type RightsRequestGraphNodeQuery = {
|
||||
response: RightsRequestGraphNodeQuery$data;
|
||||
variables: RightsRequestGraphNodeQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "rightsRequestId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "rightsRequestId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestType",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestState",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "dataSubject",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "contact",
|
||||
"storageKey": null
|
||||
},
|
||||
v7 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "details",
|
||||
"storageKey": null
|
||||
},
|
||||
v8 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
v9 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "actionTaken",
|
||||
"storageKey": null
|
||||
},
|
||||
v10 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Organization",
|
||||
"kind": "LinkedField",
|
||||
"name": "organization",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v11 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
v12 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RightsRequestGraphNodeQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/),
|
||||
(v12/*: any*/)
|
||||
],
|
||||
"type": "RightsRequest",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "RightsRequestGraphNodeQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/),
|
||||
(v12/*: any*/)
|
||||
],
|
||||
"type": "RightsRequest",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "93aa5fb44159c0ae2709c34f493bc69f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RightsRequestGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query RightsRequestGraphNodeQuery(\n $rightsRequestId: ID!\n) {\n node(id: $rightsRequestId) {\n __typename\n ... on RightsRequest {\n id\n requestType\n requestState\n dataSubject\n contact\n details\n deadline\n actionTaken\n organization {\n id\n name\n }\n createdAt\n updatedAt\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "9f7d09c59f937a8de6cfbc0354b42d1b";
|
||||
|
||||
export default node;
|
||||
178
apps/console/src/hooks/graph/__generated__/RightsRequestGraphUpdateMutation.graphql.ts
generated
Normal file
178
apps/console/src/hooks/graph/__generated__/RightsRequestGraphUpdateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* @generated SignedSource<<df8070312cba4b83a8f30231ac7b7fde>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RightsRequestState = "DONE" | "IN_PROGRESS" | "TODO";
|
||||
export type RightsRequestType = "ACCESS" | "DELETION" | "PORTABILITY";
|
||||
export type UpdateRightsRequestInput = {
|
||||
actionTaken?: string | null | undefined;
|
||||
contact?: string | null | undefined;
|
||||
dataSubject?: string | null | undefined;
|
||||
deadline?: any | null | undefined;
|
||||
details?: string | null | undefined;
|
||||
id: string;
|
||||
requestState?: RightsRequestState | null | undefined;
|
||||
requestType?: RightsRequestType | null | undefined;
|
||||
};
|
||||
export type RightsRequestGraphUpdateMutation$variables = {
|
||||
input: UpdateRightsRequestInput;
|
||||
};
|
||||
export type RightsRequestGraphUpdateMutation$data = {
|
||||
readonly updateRightsRequest: {
|
||||
readonly rightsRequest: {
|
||||
readonly actionTaken: string | null | undefined;
|
||||
readonly contact: string | null | undefined;
|
||||
readonly dataSubject: string | null | undefined;
|
||||
readonly deadline: any | null | undefined;
|
||||
readonly details: string | null | undefined;
|
||||
readonly id: string;
|
||||
readonly requestState: RightsRequestState;
|
||||
readonly requestType: RightsRequestType;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type RightsRequestGraphUpdateMutation = {
|
||||
response: RightsRequestGraphUpdateMutation$data;
|
||||
variables: RightsRequestGraphUpdateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "UpdateRightsRequestPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateRightsRequest",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequest",
|
||||
"kind": "LinkedField",
|
||||
"name": "rightsRequest",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestState",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "dataSubject",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "contact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "details",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "actionTaken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RightsRequestGraphUpdateMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "RightsRequestGraphUpdateMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "4b06cb49236bb946cfa333e95b80f2a8",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RightsRequestGraphUpdateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation RightsRequestGraphUpdateMutation(\n $input: UpdateRightsRequestInput!\n) {\n updateRightsRequest(input: $input) {\n rightsRequest {\n id\n requestType\n requestState\n dataSubject\n contact\n details\n deadline\n actionTaken\n updatedAt\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "f2f42ebc57de95ec4a760e7d701f90fa";
|
||||
|
||||
export default node;
|
||||
@@ -224,6 +224,13 @@ function MainLayoutContent({
|
||||
to={`${prefix}/processing-activities`}
|
||||
/>
|
||||
)}
|
||||
{isAuthorized("Organization", "listRightsRequests") && (
|
||||
<SidebarItem
|
||||
label={__("Rights Requests")}
|
||||
icon={IconLock}
|
||||
to={`${prefix}/rights-requests`}
|
||||
/>
|
||||
)}
|
||||
{isAuthorized("Organization", "listSnapshots") && (
|
||||
<SidebarItem
|
||||
label={__("Snapshots")}
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
import {
|
||||
ConnectionHandler,
|
||||
usePreloadedQuery,
|
||||
type PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import {
|
||||
rightsRequestNodeQuery,
|
||||
useDeleteRightsRequest,
|
||||
useUpdateRightsRequest,
|
||||
RightsRequestsConnectionKey,
|
||||
} from "../../../hooks/graph/RightsRequestGraph";
|
||||
import {
|
||||
ActionDropdown,
|
||||
Badge,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
DropdownItem,
|
||||
Field,
|
||||
Option,
|
||||
Input,
|
||||
Card,
|
||||
Textarea,
|
||||
useToast,
|
||||
Select,
|
||||
Label,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { Controller } from "react-hook-form";
|
||||
import {
|
||||
formatError,
|
||||
type GraphQLError,
|
||||
formatDatetime,
|
||||
toDateInput,
|
||||
getRightsRequestTypeLabel,
|
||||
getRightsRequestTypeOptions,
|
||||
getRightsRequestStateVariant,
|
||||
getRightsRequestStateLabel,
|
||||
getRightsRequestStateOptions,
|
||||
} from "@probo/helpers";
|
||||
import z from "zod";
|
||||
import type { RightsRequestGraphNodeQuery } from "/hooks/graph/__generated__/RightsRequestGraphNodeQuery.graphql";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
|
||||
const updateRequestSchema = z.object({
|
||||
requestType: z.enum(["ACCESS", "DELETION", "PORTABILITY"]),
|
||||
requestState: z.enum(["TODO", "IN_PROGRESS", "DONE"]),
|
||||
dataSubject: z.string().optional(),
|
||||
contact: z.string().optional(),
|
||||
details: z.string().optional(),
|
||||
deadline: z.string().optional(),
|
||||
actionTaken: z.string().optional(),
|
||||
});
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<RightsRequestGraphNodeQuery>;
|
||||
};
|
||||
|
||||
export default function RightsRequestDetailsPage(props: Props) {
|
||||
const data = usePreloadedQuery<RightsRequestGraphNodeQuery>(rightsRequestNodeQuery, props.queryRef);
|
||||
const request = data.node;
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const organizationId = useOrganizationId();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
const updateRequest = useUpdateRightsRequest();
|
||||
|
||||
const connectionId = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
RightsRequestsConnectionKey
|
||||
);
|
||||
|
||||
const deleteRequest = useDeleteRightsRequest({ id: request.id! }, connectionId);
|
||||
|
||||
const { register, handleSubmit, formState, control } = useFormWithSchema(
|
||||
updateRequestSchema,
|
||||
{
|
||||
defaultValues: {
|
||||
requestType: request.requestType || "ACCESS",
|
||||
requestState: request.requestState || "TODO",
|
||||
dataSubject: request.dataSubject || "",
|
||||
contact: request.contact || "",
|
||||
details: request.details || "",
|
||||
deadline: toDateInput(request.deadline),
|
||||
actionTaken: request.actionTaken || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const onSubmit = handleSubmit(async (formData) => {
|
||||
try {
|
||||
await updateRequest({
|
||||
id: request.id!,
|
||||
requestType: formData.requestType,
|
||||
requestState: formData.requestState,
|
||||
dataSubject: formData.dataSubject || undefined,
|
||||
contact: formData.contact || undefined,
|
||||
details: formData.details || undefined,
|
||||
deadline: formatDatetime(formData.deadline) ?? null,
|
||||
actionTaken: formData.actionTaken || undefined,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Rights request updated successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Failed to update rights request"), error as GraphQLError),
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const typeOptions = getRightsRequestTypeOptions(__);
|
||||
const stateOptions = getRightsRequestStateOptions(__);
|
||||
|
||||
const breadcrumbRequestsUrl = `/organizations/${organizationId}/rights-requests`;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ label: __("Rights Requests"), to: breadcrumbRequestsUrl },
|
||||
{ label: request.dataSubject || request.id! },
|
||||
]}
|
||||
/>
|
||||
{isAuthorized("RightsRequest", "deleteRightsRequest") && (
|
||||
<ActionDropdown>
|
||||
<DropdownItem onClick={deleteRequest} variant="danger">
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<div className="p-6">
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<h1 className="text-2xl font-bold">{getRightsRequestTypeLabel(__, request.requestType || "ACCESS")}</h1>
|
||||
<Badge variant="neutral">{getRightsRequestTypeLabel(__, request.requestType || "ACCESS")}</Badge>
|
||||
<Badge variant={getRightsRequestStateVariant(request.requestState || "TODO")}>
|
||||
{getRightsRequestStateLabel(__, request.requestState || "TODO")}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={onSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Controller
|
||||
control={control}
|
||||
name="requestType"
|
||||
render={({ field }) => (
|
||||
<div>
|
||||
<Label>{__("Request Type")} *</Label>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
{typeOptions.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{formState.errors.requestType?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.requestType.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="requestState"
|
||||
render={({ field }) => (
|
||||
<div>
|
||||
<Label>{__("State")} *</Label>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
{stateOptions.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{formState.errors.requestState?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.requestState.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Field
|
||||
label={__("Data Subject")}
|
||||
{...register("dataSubject")}
|
||||
error={formState.errors.dataSubject?.message}
|
||||
/>
|
||||
|
||||
<Field
|
||||
label={__("Contact")}
|
||||
{...register("contact")}
|
||||
error={formState.errors.contact?.message}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Label>{__("Details")}</Label>
|
||||
<Textarea
|
||||
{...register("details")}
|
||||
placeholder={__("Enter request details")}
|
||||
rows={3}
|
||||
/>
|
||||
{formState.errors.details?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.details.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>{__("Deadline")}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
{...register("deadline")}
|
||||
/>
|
||||
{formState.errors.deadline?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.deadline.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{__("Action Taken")}</Label>
|
||||
<Textarea
|
||||
{...register("actionTaken")}
|
||||
placeholder={__("Enter action taken")}
|
||||
rows={3}
|
||||
/>
|
||||
{formState.errors.actionTaken?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.actionTaken.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-4">
|
||||
{isAuthorized("RightsRequest", "updateRightsRequest") && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={formState.isSubmitting}
|
||||
>
|
||||
{formState.isSubmitting ? __("Saving...") : __("Save Changes")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
import {
|
||||
Button,
|
||||
IconPlusLarge,
|
||||
PageHeader,
|
||||
Card,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
Badge,
|
||||
ActionDropdown,
|
||||
DropdownItem,
|
||||
IconTrashCan,
|
||||
Table,
|
||||
useConfirm,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import {
|
||||
ConnectionHandler,
|
||||
graphql,
|
||||
usePaginationFragment,
|
||||
usePreloadedQuery,
|
||||
useMutation,
|
||||
type PreloadedQuery,
|
||||
} from "react-relay";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { CreateRightsRequestDialog } from "./dialogs/CreateRightsRequestDialog";
|
||||
import { deleteRightsRequestMutation, RightsRequestsConnectionKey, rightsRequestsQuery } from "../../../hooks/graph/RightsRequestGraph";
|
||||
import {
|
||||
sprintf,
|
||||
promisifyMutation,
|
||||
formatDate,
|
||||
getRightsRequestTypeLabel,
|
||||
getRightsRequestStateVariant,
|
||||
getRightsRequestStateLabel,
|
||||
} from "@probo/helpers";
|
||||
import type { NodeOf } from "/types";
|
||||
import type {
|
||||
RightsRequestsPageFragment$key,
|
||||
RightsRequestsPageFragment$data,
|
||||
} from "./__generated__/RightsRequestsPageFragment.graphql";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
import type { RightsRequestGraphListQuery } from "/hooks/graph/__generated__/RightsRequestGraphListQuery.graphql";
|
||||
|
||||
interface RightsRequestsPageProps {
|
||||
queryRef: PreloadedQuery<RightsRequestGraphListQuery>;
|
||||
}
|
||||
|
||||
const rightsRequestsPageFragment = graphql`
|
||||
fragment RightsRequestsPageFragment on Organization
|
||||
@refetchable(queryName: "RightsRequestsPageRefetchQuery")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 10 }
|
||||
after: { type: "CursorKey" }
|
||||
) {
|
||||
id
|
||||
rightsRequests(
|
||||
first: $first
|
||||
after: $after
|
||||
)
|
||||
@connection(key: "RightsRequestsPage_rightsRequests") {
|
||||
__id
|
||||
totalCount
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
contact
|
||||
details
|
||||
deadline
|
||||
actionTaken
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function RightsRequestsPage({ queryRef }: RightsRequestsPageProps) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
usePageTitle(__("Rights Requests"));
|
||||
|
||||
const organization = usePreloadedQuery(
|
||||
rightsRequestsQuery,
|
||||
queryRef
|
||||
);
|
||||
|
||||
const {
|
||||
data,
|
||||
loadNext,
|
||||
hasNext,
|
||||
isLoadingNext,
|
||||
} = usePaginationFragment<
|
||||
RightsRequestGraphListQuery,
|
||||
RightsRequestsPageFragment$key
|
||||
>(rightsRequestsPageFragment, organization.node);
|
||||
|
||||
const connectionId = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
RightsRequestsConnectionKey
|
||||
);
|
||||
const requests = data?.rightsRequests?.edges?.map((edge) => edge.node) ?? [];
|
||||
|
||||
const hasAnyAction = (
|
||||
isAuthorized("RightsRequest", "updateRightsRequest") ||
|
||||
isAuthorized("RightsRequest", "deleteRightsRequest")
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader title={__("Rights Requests")} description={__("Manage data subject rights requests.")}>
|
||||
{isAuthorized("Organization", "createRightsRequest") && (
|
||||
<CreateRightsRequestDialog
|
||||
organizationId={organizationId}
|
||||
connectionId={connectionId}
|
||||
>
|
||||
<Button icon={IconPlusLarge}>
|
||||
{__("Add rights request")}
|
||||
</Button>
|
||||
</CreateRightsRequestDialog>
|
||||
)}
|
||||
</PageHeader>
|
||||
|
||||
{requests.length > 0 ? (
|
||||
<Card>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Type")}</Th>
|
||||
<Th>{__("State")}</Th>
|
||||
<Th>{__("Data Subject")}</Th>
|
||||
<Th>{__("Contact")}</Th>
|
||||
<Th>{__("Deadline")}</Th>
|
||||
{hasAnyAction && <Th>{__("Actions")}</Th>}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{requests.map((request) => (
|
||||
<RequestRow
|
||||
key={request.id}
|
||||
request={request}
|
||||
connectionId={connectionId}
|
||||
hasAnyAction={hasAnyAction}
|
||||
/>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
|
||||
{hasNext && (
|
||||
<div className="p-4 border-t">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => loadNext(10)}
|
||||
disabled={isLoadingNext}
|
||||
>
|
||||
{isLoadingNext ? __("Loading...") : __("Load more")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
) : (
|
||||
<Card padded>
|
||||
<div className="text-center py-12">
|
||||
<h3 className="text-lg font-semibold mb-2">
|
||||
{__("No rights requests yet")}
|
||||
</h3>
|
||||
<p className="text-txt-tertiary mb-4">
|
||||
{__("Create your first rights request to get started.")}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RequestRow({
|
||||
request,
|
||||
connectionId,
|
||||
hasAnyAction,
|
||||
}: {
|
||||
request: NodeOf<NonNullable<RightsRequestsPageFragment$data['rightsRequests']>>;
|
||||
connectionId: string;
|
||||
hasAnyAction: boolean;
|
||||
}) {
|
||||
const organizationId = useOrganizationId();
|
||||
const { __ } = useTranslate();
|
||||
const [deleteRequest] = useMutation(deleteRightsRequestMutation);
|
||||
const confirm = useConfirm();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
const handleDelete = () => {
|
||||
confirm(
|
||||
() =>
|
||||
promisifyMutation(deleteRequest)({
|
||||
variables: {
|
||||
input: {
|
||||
rightsRequestId: request.id,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
}),
|
||||
{
|
||||
message: sprintf(
|
||||
__(
|
||||
"This will permanently delete the rights request. This action cannot be undone."
|
||||
)
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const detailsUrl = `/organizations/${organizationId}/rights-requests/${request.id}`;
|
||||
|
||||
return (
|
||||
<Tr to={detailsUrl}>
|
||||
<Td>
|
||||
<Badge variant="neutral">{getRightsRequestTypeLabel(__, request.requestType)}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Badge variant={getRightsRequestStateVariant(request.requestState)}>
|
||||
{getRightsRequestStateLabel(__, request.requestState)}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td>{request.dataSubject || "-"}</Td>
|
||||
<Td>{request.contact || "-"}</Td>
|
||||
<Td>
|
||||
{request.deadline ? (
|
||||
<time dateTime={request.deadline}>
|
||||
{formatDate(request.deadline)}
|
||||
</time>
|
||||
) : (
|
||||
<span className="text-txt-tertiary">{__("No deadline")}</span>
|
||||
)}
|
||||
</Td>
|
||||
{hasAnyAction && (
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
{isAuthorized("RightsRequest", "deleteRightsRequest") && (
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
variant="danger"
|
||||
onSelect={handleDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
)}
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
)}
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
267
apps/console/src/pages/organizations/rightsRequests/__generated__/RightsRequestsPageFragment.graphql.ts
generated
Normal file
267
apps/console/src/pages/organizations/rightsRequests/__generated__/RightsRequestsPageFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* @generated SignedSource<<4b94130573ce20c5879e717c00128fbb>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type RightsRequestState = "DONE" | "IN_PROGRESS" | "TODO";
|
||||
export type RightsRequestType = "ACCESS" | "DELETION" | "PORTABILITY";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type RightsRequestsPageFragment$data = {
|
||||
readonly id: string;
|
||||
readonly rightsRequests: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly actionTaken: string | null | undefined;
|
||||
readonly contact: string | null | undefined;
|
||||
readonly createdAt: any;
|
||||
readonly dataSubject: string | null | undefined;
|
||||
readonly deadline: any | null | undefined;
|
||||
readonly details: string | null | undefined;
|
||||
readonly id: string;
|
||||
readonly requestState: RightsRequestState;
|
||||
readonly requestType: RightsRequestType;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
readonly pageInfo: {
|
||||
readonly endCursor: any | null | undefined;
|
||||
readonly hasNextPage: boolean;
|
||||
};
|
||||
readonly totalCount: number;
|
||||
};
|
||||
readonly " $fragmentType": "RightsRequestsPageFragment";
|
||||
};
|
||||
export type RightsRequestsPageFragment$key = {
|
||||
readonly " $data"?: RightsRequestsPageFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"RightsRequestsPageFragment">;
|
||||
};
|
||||
|
||||
import RightsRequestsPageRefetchQuery_graphql from './RightsRequestsPageRefetchQuery.graphql';
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = [
|
||||
"rightsRequests"
|
||||
],
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
{
|
||||
"defaultValue": 10,
|
||||
"kind": "LocalArgument",
|
||||
"name": "first"
|
||||
}
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": "first",
|
||||
"cursor": "after",
|
||||
"direction": "forward",
|
||||
"path": (v0/*: any*/)
|
||||
}
|
||||
],
|
||||
"refetch": {
|
||||
"connection": {
|
||||
"forward": {
|
||||
"count": "first",
|
||||
"cursor": "after"
|
||||
},
|
||||
"backward": null,
|
||||
"path": (v0/*: any*/)
|
||||
},
|
||||
"fragmentPathInResult": [
|
||||
"node"
|
||||
],
|
||||
"operation": RightsRequestsPageRefetchQuery_graphql,
|
||||
"identifierInfo": {
|
||||
"identifierField": "id",
|
||||
"identifierQueryVariableName": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "RightsRequestsPageFragment",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": "rightsRequests",
|
||||
"args": null,
|
||||
"concreteType": "RightsRequestConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__RightsRequestsPage_rightsRequests_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalCount",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequestEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequest",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestState",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "dataSubject",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "contact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "details",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "actionTaken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3476c393dc97ae3d730ed5715922e987";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* @generated SignedSource<<dddf70832ae8eecef831e8832a782f20>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type RightsRequestsPageRefetchQuery$variables = {
|
||||
after?: any | null | undefined;
|
||||
first?: number | null | undefined;
|
||||
id: string;
|
||||
};
|
||||
export type RightsRequestsPageRefetchQuery$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"RightsRequestsPageFragment">;
|
||||
};
|
||||
};
|
||||
export type RightsRequestsPageRefetchQuery = {
|
||||
response: RightsRequestsPageRefetchQuery$data;
|
||||
variables: RightsRequestsPageRefetchQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
{
|
||||
"defaultValue": 10,
|
||||
"kind": "LocalArgument",
|
||||
"name": "first"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "id"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "id"
|
||||
}
|
||||
],
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "after",
|
||||
"variableName": "after"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "first",
|
||||
"variableName": "first"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "RightsRequestsPageRefetchQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": (v2/*: any*/),
|
||||
"kind": "FragmentSpread",
|
||||
"name": "RightsRequestsPageFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "RightsRequestsPageRefetchQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "RightsRequestConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "rightsRequests",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "totalCount",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequestEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "RightsRequest",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "requestState",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "dataSubject",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "contact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "details",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deadline",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "actionTaken",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "RightsRequestsPage_rightsRequests",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "rightsRequests"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "10c7e9a6f7813d150e34282befd799ff",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "RightsRequestsPageRefetchQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query RightsRequestsPageRefetchQuery(\n $after: CursorKey\n $first: Int = 10\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RightsRequestsPageFragment_2HEEH6\n id\n }\n}\n\nfragment RightsRequestsPageFragment_2HEEH6 on Organization {\n id\n rightsRequests(first: $first, after: $after) {\n totalCount\n edges {\n node {\n id\n requestType\n requestState\n dataSubject\n contact\n details\n deadline\n actionTaken\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3476c393dc97ae3d730ed5715922e987";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,235 @@
|
||||
import { type ReactNode } from "react";
|
||||
import {
|
||||
Button,
|
||||
Field,
|
||||
useToast,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
useDialogRef,
|
||||
Textarea,
|
||||
Breadcrumb,
|
||||
Label,
|
||||
Select,
|
||||
Option,
|
||||
Input,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { useCreateRightsRequest } from "../../../../hooks/graph/RightsRequestGraph";
|
||||
import { Controller } from "react-hook-form";
|
||||
import {
|
||||
formatError,
|
||||
type GraphQLError,
|
||||
formatDatetime,
|
||||
getRightsRequestTypeOptions,
|
||||
getRightsRequestStateOptions,
|
||||
} from "@probo/helpers";
|
||||
|
||||
const schema = z.object({
|
||||
requestType: z.enum(["ACCESS", "DELETION", "PORTABILITY"]),
|
||||
requestState: z.enum(["TODO", "IN_PROGRESS", "DONE"]),
|
||||
dataSubject: z.string().optional(),
|
||||
contact: z.string().optional(),
|
||||
details: z.string().optional(),
|
||||
deadline: z.string().optional(),
|
||||
actionTaken: z.string().optional(),
|
||||
});
|
||||
|
||||
type FormData = z.infer<typeof schema>;
|
||||
|
||||
interface CreateRightsRequestDialogProps {
|
||||
children: ReactNode;
|
||||
organizationId: string;
|
||||
connectionId?: string;
|
||||
}
|
||||
|
||||
export function CreateRightsRequestDialog({
|
||||
children,
|
||||
organizationId,
|
||||
connectionId,
|
||||
}: CreateRightsRequestDialogProps) {
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const createRequest = useCreateRightsRequest(connectionId || "");
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } = useFormWithSchema(schema, {
|
||||
defaultValues: {
|
||||
requestType: "ACCESS" as const,
|
||||
requestState: "TODO" as const,
|
||||
dataSubject: "",
|
||||
contact: "",
|
||||
details: "",
|
||||
deadline: "",
|
||||
actionTaken: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit(async (formData: FormData) => {
|
||||
try {
|
||||
await createRequest({
|
||||
organizationId,
|
||||
requestType: formData.requestType,
|
||||
requestState: formData.requestState,
|
||||
dataSubject: formData.dataSubject || undefined,
|
||||
contact: formData.contact || undefined,
|
||||
details: formData.details || undefined,
|
||||
deadline: formatDatetime(formData.deadline),
|
||||
actionTaken: formData.actionTaken || undefined,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Rights request created successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
|
||||
reset();
|
||||
dialogRef.current?.close();
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(__("Failed to create rights request"), error as GraphQLError),
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const typeOptions = getRightsRequestTypeOptions(__);
|
||||
const stateOptions = getRightsRequestStateOptions(__);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
trigger={children}
|
||||
title={<Breadcrumb items={[__("Rights Requests"), __("Create Request")]} />}
|
||||
className="max-w-2xl"
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent padded className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Controller
|
||||
control={control}
|
||||
name="requestType"
|
||||
render={({ field }) => (
|
||||
<div>
|
||||
<Label>{__("Request Type")} *</Label>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
{typeOptions.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{formState.errors.requestType?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.requestType.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="requestState"
|
||||
render={({ field }) => (
|
||||
<div>
|
||||
<Label>{__("State")} *</Label>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
{stateOptions.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{formState.errors.requestState?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.requestState.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Field
|
||||
label={__("Data Subject")}
|
||||
{...register("dataSubject")}
|
||||
placeholder={__("Enter data subject name")}
|
||||
error={formState.errors.dataSubject?.message}
|
||||
/>
|
||||
|
||||
<Field
|
||||
label={__("Contact")}
|
||||
{...register("contact")}
|
||||
placeholder={__("Enter contact information")}
|
||||
error={formState.errors.contact?.message}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Label>{__("Details")}</Label>
|
||||
<Textarea
|
||||
{...register("details")}
|
||||
placeholder={__("Enter request details")}
|
||||
rows={3}
|
||||
/>
|
||||
{formState.errors.details?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.details.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>{__("Deadline")}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
{...register("deadline")}
|
||||
/>
|
||||
{formState.errors.deadline?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.deadline.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{__("Action Taken")}</Label>
|
||||
<Textarea
|
||||
{...register("actionTaken")}
|
||||
placeholder={__("Enter action taken")}
|
||||
rows={3}
|
||||
/>
|
||||
{formState.errors.actionTaken?.message && (
|
||||
<div className="text-red-500 text-sm mt-1">
|
||||
{formState.errors.actionTaken.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={formState.isSubmitting}
|
||||
>
|
||||
{formState.isSubmitting ? __("Creating...") : __("Create Request")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import { nonconformityRoutes } from "./routes/nonconformityRoutes.ts";
|
||||
import { obligationRoutes } from "./routes/obligationRoutes.ts";
|
||||
import { snapshotsRoutes } from "./routes/snapshotsRoutes.ts";
|
||||
import { continualImprovementRoutes } from "./routes/continualImprovementRoutes.ts";
|
||||
import { rightsRequestRoutes } from "./routes/rightsRequestRoutes.ts";
|
||||
import { processingActivityRoutes } from "./routes/processingActivityRoutes.ts";
|
||||
import { lazy } from "@probo/react-lazy";
|
||||
import { loaderFromQueryLoader, routeFromAppRoute, withQueryRef, type AppRoute } from "@probo/routes";
|
||||
@@ -224,6 +225,7 @@ const routes = [
|
||||
...nonconformityRoutes,
|
||||
...obligationRoutes,
|
||||
...continualImprovementRoutes,
|
||||
...rightsRequestRoutes,
|
||||
...processingActivityRoutes,
|
||||
...snapshotsRoutes,
|
||||
...trustCenterRoutes,
|
||||
|
||||
35
apps/console/src/routes/rightsRequestRoutes.ts
Normal file
35
apps/console/src/routes/rightsRequestRoutes.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { loadQuery } from "react-relay";
|
||||
import { relayEnvironment } from "/providers/RelayProviders";
|
||||
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
|
||||
import { lazy } from "@probo/react-lazy";
|
||||
import { rightsRequestsQuery, rightsRequestNodeQuery } from "/hooks/graph/RightsRequestGraph";
|
||||
import type { RightsRequestGraphListQuery } from "/hooks/graph/__generated__/RightsRequestGraphListQuery.graphql";
|
||||
import type { RightsRequestGraphNodeQuery } from "/hooks/graph/__generated__/RightsRequestGraphNodeQuery.graphql";
|
||||
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
|
||||
|
||||
export const rightsRequestRoutes = [
|
||||
{
|
||||
path: "rights-requests",
|
||||
Fallback: PageSkeleton,
|
||||
loader: loaderFromQueryLoader(({ organizationId }) =>
|
||||
loadQuery<RightsRequestGraphListQuery>(relayEnvironment, rightsRequestsQuery, {
|
||||
organizationId,
|
||||
}),
|
||||
),
|
||||
Component: withQueryRef(lazy(
|
||||
() => import("/pages/organizations/rightsRequests/RightsRequestsPage")
|
||||
)),
|
||||
},
|
||||
{
|
||||
path: "rights-requests/:requestId",
|
||||
Fallback: PageSkeleton,
|
||||
loader: loaderFromQueryLoader(({ requestId }) =>
|
||||
loadQuery<RightsRequestGraphNodeQuery>(relayEnvironment, rightsRequestNodeQuery, {
|
||||
rightsRequestId: requestId!,
|
||||
}),
|
||||
),
|
||||
Component: withQueryRef(lazy(
|
||||
() => import("/pages/organizations/rightsRequests/RightsRequestDetailsPage")
|
||||
)),
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
394
e2e/console/rights_request_test.go
Normal file
394
e2e/console/rights_request_test.go
Normal file
@@ -0,0 +1,394 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package console_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.probo.inc/probo/e2e/internal/testutil"
|
||||
)
|
||||
|
||||
func TestRightsRequest_Create(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
query := `
|
||||
mutation CreateRightsRequest($input: CreateRightsRequestInput!) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge {
|
||||
node {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
contact
|
||||
details
|
||||
actionTaken
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
CreateRightsRequest struct {
|
||||
RightsRequestEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
RequestType string `json:"requestType"`
|
||||
RequestState string `json:"requestState"`
|
||||
DataSubject string `json:"dataSubject"`
|
||||
Contact string `json:"contact"`
|
||||
Details string `json:"details"`
|
||||
ActionTaken string `json:"actionTaken"`
|
||||
} `json:"node"`
|
||||
} `json:"rightsRequestEdge"`
|
||||
} `json:"createRightsRequest"`
|
||||
}
|
||||
|
||||
err := owner.Execute(query, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"requestType": "ACCESS",
|
||||
"requestState": "TODO",
|
||||
"dataSubject": "John Doe",
|
||||
"contact": "john.doe@example.com",
|
||||
"details": "Request access to personal data",
|
||||
"actionTaken": "Initial review completed",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := result.CreateRightsRequest.RightsRequestEdge.Node
|
||||
assert.NotEmpty(t, rr.ID)
|
||||
assert.Equal(t, "ACCESS", rr.RequestType)
|
||||
assert.Equal(t, "TODO", rr.RequestState)
|
||||
assert.Equal(t, "John Doe", rr.DataSubject)
|
||||
assert.Equal(t, "john.doe@example.com", rr.Contact)
|
||||
assert.Equal(t, "Request access to personal data", rr.Details)
|
||||
assert.Equal(t, "Initial review completed", rr.ActionTaken)
|
||||
}
|
||||
|
||||
func TestRightsRequest_Update(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
createQuery := `
|
||||
mutation CreateRightsRequest($input: CreateRightsRequestInput!) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var createResult struct {
|
||||
CreateRightsRequest struct {
|
||||
RightsRequestEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"node"`
|
||||
} `json:"rightsRequestEdge"`
|
||||
} `json:"createRightsRequest"`
|
||||
}
|
||||
|
||||
err := owner.Execute(createQuery, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"requestType": "ACCESS",
|
||||
"requestState": "TODO",
|
||||
"dataSubject": "Original Subject",
|
||||
"contact": "original@example.com",
|
||||
},
|
||||
}, &createResult)
|
||||
require.NoError(t, err)
|
||||
rrID := createResult.CreateRightsRequest.RightsRequestEdge.Node.ID
|
||||
|
||||
query := `
|
||||
mutation UpdateRightsRequest($input: UpdateRightsRequestInput!) {
|
||||
updateRightsRequest(input: $input) {
|
||||
rightsRequest {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
contact
|
||||
details
|
||||
actionTaken
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
UpdateRightsRequest struct {
|
||||
RightsRequest struct {
|
||||
ID string `json:"id"`
|
||||
RequestType string `json:"requestType"`
|
||||
RequestState string `json:"requestState"`
|
||||
DataSubject string `json:"dataSubject"`
|
||||
Contact string `json:"contact"`
|
||||
Details string `json:"details"`
|
||||
ActionTaken string `json:"actionTaken"`
|
||||
} `json:"rightsRequest"`
|
||||
} `json:"updateRightsRequest"`
|
||||
}
|
||||
|
||||
err = owner.Execute(query, map[string]any{
|
||||
"input": map[string]any{
|
||||
"id": rrID,
|
||||
"requestType": "DELETION",
|
||||
"requestState": "IN_PROGRESS",
|
||||
"dataSubject": "Updated Subject",
|
||||
"contact": "updated@example.com",
|
||||
"details": "Updated details",
|
||||
"actionTaken": "Processing deletion request",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, rrID, result.UpdateRightsRequest.RightsRequest.ID)
|
||||
assert.Equal(t, "DELETION", result.UpdateRightsRequest.RightsRequest.RequestType)
|
||||
assert.Equal(t, "IN_PROGRESS", result.UpdateRightsRequest.RightsRequest.RequestState)
|
||||
assert.Equal(t, "Updated Subject", result.UpdateRightsRequest.RightsRequest.DataSubject)
|
||||
assert.Equal(t, "updated@example.com", result.UpdateRightsRequest.RightsRequest.Contact)
|
||||
assert.Equal(t, "Updated details", result.UpdateRightsRequest.RightsRequest.Details)
|
||||
assert.Equal(t, "Processing deletion request", result.UpdateRightsRequest.RightsRequest.ActionTaken)
|
||||
}
|
||||
|
||||
func TestRightsRequest_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
createQuery := `
|
||||
mutation CreateRightsRequest($input: CreateRightsRequestInput!) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var createResult struct {
|
||||
CreateRightsRequest struct {
|
||||
RightsRequestEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"node"`
|
||||
} `json:"rightsRequestEdge"`
|
||||
} `json:"createRightsRequest"`
|
||||
}
|
||||
|
||||
err := owner.Execute(createQuery, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"requestType": "ACCESS",
|
||||
"requestState": "TODO",
|
||||
},
|
||||
}, &createResult)
|
||||
require.NoError(t, err)
|
||||
rrID := createResult.CreateRightsRequest.RightsRequestEdge.Node.ID
|
||||
|
||||
query := `
|
||||
mutation DeleteRightsRequest($input: DeleteRightsRequestInput!) {
|
||||
deleteRightsRequest(input: $input) {
|
||||
deletedRightsRequestId
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
DeleteRightsRequest struct {
|
||||
DeletedRightsRequestID string `json:"deletedRightsRequestId"`
|
||||
} `json:"deleteRightsRequest"`
|
||||
}
|
||||
|
||||
err = owner.Execute(query, map[string]any{
|
||||
"input": map[string]any{
|
||||
"rightsRequestId": rrID,
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, rrID, result.DeleteRightsRequest.DeletedRightsRequestID)
|
||||
}
|
||||
|
||||
func TestRightsRequest_List(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
createQuery := `
|
||||
mutation CreateRightsRequest($input: CreateRightsRequestInput!) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := owner.Do(createQuery, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"requestType": "ACCESS",
|
||||
"requestState": "TODO",
|
||||
"dataSubject": fmt.Sprintf("Subject %d", i),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
query := `
|
||||
query GetRightsRequests($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Organization {
|
||||
rightsRequests(first: 10) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
requestType
|
||||
requestState
|
||||
dataSubject
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
Node struct {
|
||||
RightsRequests struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
RequestType string `json:"requestType"`
|
||||
RequestState string `json:"requestState"`
|
||||
DataSubject string `json:"dataSubject"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
} `json:"rightsRequests"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
err := owner.Execute(query, map[string]any{
|
||||
"id": owner.GetOrganizationID().String(),
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, result.Node.RightsRequests.TotalCount, 3)
|
||||
}
|
||||
|
||||
func TestRightsRequest_TypeAndStateValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
t.Run("request type values", func(t *testing.T) {
|
||||
types := []string{"ACCESS", "DELETION", "PORTABILITY"}
|
||||
|
||||
for _, requestType := range types {
|
||||
t.Run(requestType, func(t *testing.T) {
|
||||
query := `
|
||||
mutation CreateRightsRequest($input: CreateRightsRequestInput!) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge {
|
||||
node {
|
||||
id
|
||||
requestType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
CreateRightsRequest struct {
|
||||
RightsRequestEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
RequestType string `json:"requestType"`
|
||||
} `json:"node"`
|
||||
} `json:"rightsRequestEdge"`
|
||||
} `json:"createRightsRequest"`
|
||||
}
|
||||
|
||||
err := owner.Execute(query, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"requestType": requestType,
|
||||
"requestState": "TODO",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, requestType, result.CreateRightsRequest.RightsRequestEdge.Node.RequestType)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("request state values", func(t *testing.T) {
|
||||
states := []string{"TODO", "IN_PROGRESS", "DONE"}
|
||||
|
||||
for _, requestState := range states {
|
||||
t.Run(requestState, func(t *testing.T) {
|
||||
query := `
|
||||
mutation CreateRightsRequest($input: CreateRightsRequestInput!) {
|
||||
createRightsRequest(input: $input) {
|
||||
rightsRequestEdge {
|
||||
node {
|
||||
id
|
||||
requestState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
CreateRightsRequest struct {
|
||||
RightsRequestEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
RequestState string `json:"requestState"`
|
||||
} `json:"node"`
|
||||
} `json:"rightsRequestEdge"`
|
||||
} `json:"createRightsRequest"`
|
||||
}
|
||||
|
||||
err := owner.Execute(query, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"requestType": "ACCESS",
|
||||
"requestState": requestState,
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, requestState, result.CreateRightsRequest.RightsRequestEdge.Node.RequestState)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -71,3 +71,14 @@ export {
|
||||
getTrustCenterDocumentAccessStatusLabel,
|
||||
type TrustCenterDocumentAccessInfo,
|
||||
} from "./trustCenterDocumentAccess";
|
||||
export {
|
||||
getRightsRequestTypeLabel,
|
||||
getRightsRequestTypeOptions,
|
||||
getRightsRequestStateVariant,
|
||||
getRightsRequestStateLabel,
|
||||
getRightsRequestStateOptions,
|
||||
rightsRequestTypes,
|
||||
rightsRequestStates,
|
||||
type RightsRequestType,
|
||||
type RightsRequestState,
|
||||
} from "./rightsRequest";
|
||||
|
||||
80
packages/helpers/src/rightsRequest.ts
Normal file
80
packages/helpers/src/rightsRequest.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
type Translator = (s: string) => string;
|
||||
|
||||
export type RightsRequestType = "ACCESS" | "DELETION" | "PORTABILITY";
|
||||
|
||||
export const rightsRequestTypes = [
|
||||
"ACCESS",
|
||||
"DELETION",
|
||||
"PORTABILITY",
|
||||
] as const;
|
||||
|
||||
export type RightsRequestState = "TODO" | "IN_PROGRESS" | "DONE";
|
||||
|
||||
export const rightsRequestStates = [
|
||||
"TODO",
|
||||
"IN_PROGRESS",
|
||||
"DONE",
|
||||
] as const;
|
||||
|
||||
export function getRightsRequestTypeLabel(__: Translator, type: RightsRequestType) {
|
||||
switch (type) {
|
||||
case "ACCESS":
|
||||
return __("Access");
|
||||
case "DELETION":
|
||||
return __("Deletion");
|
||||
case "PORTABILITY":
|
||||
return __("Portability");
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
export function getRightsRequestTypeOptions(__: Translator) {
|
||||
return rightsRequestTypes.map((type) => ({
|
||||
value: type,
|
||||
label: __({
|
||||
"ACCESS": "Access",
|
||||
"DELETION": "Deletion",
|
||||
"PORTABILITY": "Portability",
|
||||
}[type]),
|
||||
}));
|
||||
}
|
||||
|
||||
export const getRightsRequestStateVariant = (
|
||||
state: RightsRequestState
|
||||
): "danger" | "warning" | "success" | "neutral" | "info" | "outline" | "highlight" => {
|
||||
switch (state) {
|
||||
case "TODO":
|
||||
return "warning" as const;
|
||||
case "IN_PROGRESS":
|
||||
return "info" as const;
|
||||
case "DONE":
|
||||
return "success" as const;
|
||||
default:
|
||||
return "neutral" as const;
|
||||
}
|
||||
};
|
||||
|
||||
export function getRightsRequestStateLabel(__: Translator, state: RightsRequestState) {
|
||||
switch (state) {
|
||||
case "TODO":
|
||||
return __("To Do");
|
||||
case "IN_PROGRESS":
|
||||
return __("In Progress");
|
||||
case "DONE":
|
||||
return __("Done");
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export function getRightsRequestStateOptions(__: Translator) {
|
||||
return rightsRequestStates.map((state) => ({
|
||||
value: state,
|
||||
label: __({
|
||||
"TODO": "To Do",
|
||||
"IN_PROGRESS": "In Progress",
|
||||
"DONE": "Done",
|
||||
}[state]),
|
||||
}));
|
||||
}
|
||||
@@ -92,6 +92,7 @@ const (
|
||||
ActionListComplianceReports Action = "listComplianceReports"
|
||||
ActionListContacts Action = "listContacts"
|
||||
ActionListContinualImprovements Action = "listContinualImprovements"
|
||||
ActionListRightsRequests Action = "listRightsRequests"
|
||||
ActionListControls Action = "listControls"
|
||||
ActionListData Action = "listData"
|
||||
ActionListDocuments Action = "listDocuments"
|
||||
@@ -122,6 +123,7 @@ const (
|
||||
ActionCreateAsset Action = "createAsset"
|
||||
ActionCreateAudit Action = "createAudit"
|
||||
ActionCreateContinualImprovement Action = "createContinualImprovement"
|
||||
ActionCreateRightsRequest Action = "createRightsRequest"
|
||||
ActionCreateControl Action = "createControl"
|
||||
ActionCreateControlAuditMapping Action = "createControlAuditMapping"
|
||||
ActionCreateControlDocumentMapping Action = "createControlDocumentMapping"
|
||||
@@ -159,6 +161,7 @@ const (
|
||||
ActionUpdateAsset Action = "updateAsset"
|
||||
ActionUpdateAudit Action = "updateAudit"
|
||||
ActionUpdateContinualImprovement Action = "updateContinualImprovement"
|
||||
ActionUpdateRightsRequest Action = "updateRightsRequest"
|
||||
ActionUpdateControl Action = "updateControl"
|
||||
ActionUpdateDatum Action = "updateDatum"
|
||||
ActionUpdateDocument Action = "updateDocument"
|
||||
@@ -191,6 +194,7 @@ const (
|
||||
ActionDeleteAudit Action = "deleteAudit"
|
||||
ActionDeleteAuditReport Action = "deleteAuditReport"
|
||||
ActionDeleteContinualImprovement Action = "deleteContinualImprovement"
|
||||
ActionDeleteRightsRequest Action = "deleteRightsRequest"
|
||||
ActionDeleteControl Action = "deleteControl"
|
||||
ActionDeleteControlAuditMapping Action = "deleteControlAuditMapping"
|
||||
ActionDeleteControlDocumentMapping Action = "deleteControlDocumentMapping"
|
||||
@@ -299,6 +303,7 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionListNonconformities: NonEmployeeRoles,
|
||||
ActionListObligations: NonEmployeeRoles,
|
||||
ActionListContinualImprovements: NonEmployeeRoles,
|
||||
ActionListRightsRequests: NonEmployeeRoles,
|
||||
ActionListProcessingActivities: NonEmployeeRoles,
|
||||
ActionListSnapshots: NonEmployeeRoles,
|
||||
ActionConfirmEmail: NonEmployeeRoles,
|
||||
@@ -337,6 +342,7 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionCreateNonconformity: EditRoles,
|
||||
ActionCreateObligation: EditRoles,
|
||||
ActionCreateContinualImprovement: EditRoles,
|
||||
ActionCreateRightsRequest: EditRoles,
|
||||
ActionCreateProcessingActivity: EditRoles,
|
||||
ActionCreateSnapshot: EditRoles,
|
||||
ActionCreateTrustCenterFile: EditRoles,
|
||||
@@ -674,6 +680,13 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionUpdateContinualImprovement: EditRoles,
|
||||
ActionDeleteContinualImprovement: EditRoles,
|
||||
},
|
||||
coredata.RightsRequestEntityType: {
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateRightsRequest: EditRoles,
|
||||
ActionDeleteRightsRequest: EditRoles,
|
||||
},
|
||||
coredata.ProcessingActivityEntityType: {
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
|
||||
@@ -69,6 +69,7 @@ const (
|
||||
MeetingEntityType uint16 = 45
|
||||
DataProtectionImpactAssessmentEntityType uint16 = 46
|
||||
TransferImpactAssessmentEntityType uint16 = 47
|
||||
RightsRequestEntityType uint16 = 48
|
||||
)
|
||||
|
||||
type EntityInfo struct {
|
||||
@@ -269,6 +270,10 @@ var entityRegistry = map[uint16]EntityInfo{
|
||||
Model: "TransferImpactAssessment",
|
||||
Table: "processing_activity_transfer_impact_assessments",
|
||||
},
|
||||
RightsRequestEntityType: {
|
||||
Model: "RightsRequest",
|
||||
Table: "rights_requests",
|
||||
},
|
||||
}
|
||||
|
||||
func EntityTable(entityType uint16) (string, bool) {
|
||||
|
||||
37
pkg/coredata/migrations/20251226T130238Z.sql
Normal file
37
pkg/coredata/migrations/20251226T130238Z.sql
Normal file
@@ -0,0 +1,37 @@
|
||||
CREATE TYPE rights_request_type AS ENUM (
|
||||
'ACCESS',
|
||||
'DELETION',
|
||||
'PORTABILITY'
|
||||
);
|
||||
|
||||
CREATE TYPE rights_request_state AS ENUM (
|
||||
'TODO',
|
||||
'IN_PROGRESS',
|
||||
'DONE'
|
||||
);
|
||||
|
||||
CREATE TABLE rights_requests (
|
||||
id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
organization_id TEXT NOT NULL,
|
||||
|
||||
request_type rights_request_type NOT NULL,
|
||||
request_state rights_request_state NOT NULL,
|
||||
|
||||
data_subject TEXT,
|
||||
contact TEXT,
|
||||
details TEXT,
|
||||
|
||||
deadline DATE,
|
||||
|
||||
action_taken TEXT,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
|
||||
CONSTRAINT rights_requests_organization_id_fkey
|
||||
FOREIGN KEY (organization_id)
|
||||
REFERENCES organizations(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
41
pkg/coredata/rights_request_order_field.go
Normal file
41
pkg/coredata/rights_request_order_field.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
type RightsRequestOrderField string
|
||||
|
||||
const (
|
||||
RightsRequestOrderFieldCreatedAt RightsRequestOrderField = "CREATED_AT"
|
||||
RightsRequestOrderFieldDeadline RightsRequestOrderField = "DEADLINE"
|
||||
RightsRequestOrderFieldState RightsRequestOrderField = "STATE"
|
||||
RightsRequestOrderFieldType RightsRequestOrderField = "TYPE"
|
||||
)
|
||||
|
||||
func (p RightsRequestOrderField) Column() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func (p RightsRequestOrderField) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func (p RightsRequestOrderField) MarshalText() ([]byte, error) {
|
||||
return []byte(p.String()), nil
|
||||
}
|
||||
|
||||
func (p *RightsRequestOrderField) UnmarshalText(text []byte) error {
|
||||
*p = RightsRequestOrderField(text)
|
||||
return nil
|
||||
}
|
||||
68
pkg/coredata/rights_request_state.go
Normal file
68
pkg/coredata/rights_request_state.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type RightsRequestState string
|
||||
|
||||
const (
|
||||
RightsRequestStateTodo RightsRequestState = "TODO"
|
||||
RightsRequestStateInProgress RightsRequestState = "IN_PROGRESS"
|
||||
RightsRequestStateDone RightsRequestState = "DONE"
|
||||
)
|
||||
|
||||
func RightsRequestStates() []RightsRequestState {
|
||||
return []RightsRequestState{
|
||||
RightsRequestStateTodo,
|
||||
RightsRequestStateInProgress,
|
||||
RightsRequestStateDone,
|
||||
}
|
||||
}
|
||||
|
||||
func (rrs RightsRequestState) String() string {
|
||||
return string(rrs)
|
||||
}
|
||||
|
||||
func (rrs *RightsRequestState) Scan(value any) error {
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type for RightsRequestState: %T", value)
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "TODO":
|
||||
*rrs = RightsRequestStateTodo
|
||||
case "IN_PROGRESS":
|
||||
*rrs = RightsRequestStateInProgress
|
||||
case "DONE":
|
||||
*rrs = RightsRequestStateDone
|
||||
default:
|
||||
return fmt.Errorf("invalid RightsRequestState value: %q", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rrs RightsRequestState) Value() (driver.Value, error) {
|
||||
return string(rrs), nil
|
||||
}
|
||||
68
pkg/coredata/rights_request_type.go
Normal file
68
pkg/coredata/rights_request_type.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type RightsRequestType string
|
||||
|
||||
const (
|
||||
RightsRequestTypeAccess RightsRequestType = "ACCESS"
|
||||
RightsRequestTypeDeletion RightsRequestType = "DELETION"
|
||||
RightsRequestTypePortability RightsRequestType = "PORTABILITY"
|
||||
)
|
||||
|
||||
func RightsRequestTypes() []RightsRequestType {
|
||||
return []RightsRequestType{
|
||||
RightsRequestTypeAccess,
|
||||
RightsRequestTypeDeletion,
|
||||
RightsRequestTypePortability,
|
||||
}
|
||||
}
|
||||
|
||||
func (rrt RightsRequestType) String() string {
|
||||
return string(rrt)
|
||||
}
|
||||
|
||||
func (rrt *RightsRequestType) Scan(value any) error {
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type for RightsRequestType: %T", value)
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "ACCESS":
|
||||
*rrt = RightsRequestTypeAccess
|
||||
case "DELETION":
|
||||
*rrt = RightsRequestTypeDeletion
|
||||
case "PORTABILITY":
|
||||
*rrt = RightsRequestTypePortability
|
||||
default:
|
||||
return fmt.Errorf("invalid RightsRequestType value: %q", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rrt RightsRequestType) Value() (driver.Value, error) {
|
||||
return string(rrt), nil
|
||||
}
|
||||
327
pkg/coredata/rights_requests.go
Normal file
327
pkg/coredata/rights_requests.go
Normal file
@@ -0,0 +1,327 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
ErrRightsRequestNotFound struct {
|
||||
Identifier string
|
||||
}
|
||||
|
||||
RightsRequest struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
RequestType RightsRequestType `db:"request_type"`
|
||||
RequestState RightsRequestState `db:"request_state"`
|
||||
DataSubject *string `db:"data_subject"`
|
||||
Contact *string `db:"contact"`
|
||||
Details *string `db:"details"`
|
||||
Deadline *time.Time `db:"deadline"`
|
||||
ActionTaken *string `db:"action_taken"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
RightsRequests []*RightsRequest
|
||||
)
|
||||
|
||||
func (e ErrRightsRequestNotFound) Error() string {
|
||||
return fmt.Sprintf("rights request not found: %q", e.Identifier)
|
||||
}
|
||||
|
||||
func (rr *RightsRequest) CursorKey(field RightsRequestOrderField) page.CursorKey {
|
||||
switch field {
|
||||
case RightsRequestOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(rr.ID, rr.CreatedAt)
|
||||
case RightsRequestOrderFieldDeadline:
|
||||
return page.NewCursorKey(rr.ID, rr.Deadline)
|
||||
case RightsRequestOrderFieldState:
|
||||
return page.NewCursorKey(rr.ID, rr.RequestState)
|
||||
case RightsRequestOrderFieldType:
|
||||
return page.NewCursorKey(rr.ID, rr.RequestType)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
func (rr *RightsRequest) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
rightsRequestID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
request_type,
|
||||
request_state,
|
||||
data_subject,
|
||||
contact,
|
||||
details,
|
||||
deadline,
|
||||
action_taken,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
rights_requests
|
||||
WHERE
|
||||
%s
|
||||
AND id = @rights_request_id
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"rights_request_id": rightsRequestID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query rights request: %w", err)
|
||||
}
|
||||
|
||||
request, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[RightsRequest])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return &ErrRightsRequestNotFound{Identifier: rightsRequestID.String()}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect rights request: %w", err)
|
||||
}
|
||||
|
||||
*rr = request
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rrs *RightsRequests) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
organizationID gid.GID,
|
||||
) (int, error) {
|
||||
q := `
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
rights_requests
|
||||
WHERE
|
||||
%s
|
||||
AND organization_id = @organization_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"organization_id": organizationID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
row := conn.QueryRow(ctx, q, args)
|
||||
|
||||
var count int
|
||||
err := row.Scan(&count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot count rights requests: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (rrs *RightsRequests) LoadByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
organizationID gid.GID,
|
||||
cursor *page.Cursor[RightsRequestOrderField],
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
request_type,
|
||||
request_state,
|
||||
data_subject,
|
||||
contact,
|
||||
details,
|
||||
deadline,
|
||||
action_taken,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
rights_requests
|
||||
WHERE
|
||||
%s
|
||||
AND organization_id = @organization_id
|
||||
AND %s
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"organization_id": organizationID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query rights requests: %w", err)
|
||||
}
|
||||
|
||||
requests, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RightsRequest])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect rights requests: %w", err)
|
||||
}
|
||||
|
||||
*rrs = requests
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RightsRequest) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO rights_requests (
|
||||
id,
|
||||
tenant_id,
|
||||
organization_id,
|
||||
request_type,
|
||||
request_state,
|
||||
data_subject,
|
||||
contact,
|
||||
details,
|
||||
deadline,
|
||||
action_taken,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
@id,
|
||||
@tenant_id,
|
||||
@organization_id,
|
||||
@request_type,
|
||||
@request_state,
|
||||
@data_subject,
|
||||
@contact,
|
||||
@details,
|
||||
@deadline,
|
||||
@action_taken,
|
||||
@created_at,
|
||||
@updated_at
|
||||
)
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"id": rr.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"organization_id": rr.OrganizationID,
|
||||
"request_type": rr.RequestType,
|
||||
"request_state": rr.RequestState,
|
||||
"data_subject": rr.DataSubject,
|
||||
"contact": rr.Contact,
|
||||
"details": rr.Details,
|
||||
"deadline": rr.Deadline,
|
||||
"action_taken": rr.ActionTaken,
|
||||
"created_at": rr.CreatedAt,
|
||||
"updated_at": rr.UpdatedAt,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RightsRequest) Update(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
UPDATE rights_requests SET
|
||||
request_type = @request_type,
|
||||
request_state = @request_state,
|
||||
data_subject = @data_subject,
|
||||
contact = @contact,
|
||||
details = @details,
|
||||
deadline = @deadline,
|
||||
action_taken = @action_taken,
|
||||
updated_at = @updated_at
|
||||
WHERE
|
||||
%s
|
||||
AND id = @id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"id": rr.ID,
|
||||
"request_type": rr.RequestType,
|
||||
"request_state": rr.RequestState,
|
||||
"data_subject": rr.DataSubject,
|
||||
"contact": rr.Contact,
|
||||
"details": rr.Details,
|
||||
"deadline": rr.Deadline,
|
||||
"action_taken": rr.ActionTaken,
|
||||
"updated_at": rr.UpdatedAt,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot update rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RightsRequest) Delete(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
DELETE FROM rights_requests
|
||||
WHERE
|
||||
%s
|
||||
AND id = @id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"id": rr.ID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
291
pkg/probo/rights_request_service.go
Normal file
291
pkg/probo/rights_request_service.go
Normal file
@@ -0,0 +1,291 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package probo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"go.probo.inc/probo/pkg/validator"
|
||||
)
|
||||
|
||||
type RightsRequestService struct {
|
||||
svc *TenantService
|
||||
}
|
||||
|
||||
type (
|
||||
CreateRightsRequestRequest struct {
|
||||
OrganizationID gid.GID
|
||||
RequestType *coredata.RightsRequestType
|
||||
RequestState *coredata.RightsRequestState
|
||||
DataSubject *string
|
||||
Contact *string
|
||||
Details *string
|
||||
Deadline *time.Time
|
||||
ActionTaken *string
|
||||
}
|
||||
|
||||
UpdateRightsRequestRequest struct {
|
||||
ID gid.GID
|
||||
RequestType *coredata.RightsRequestType
|
||||
RequestState *coredata.RightsRequestState
|
||||
DataSubject **string
|
||||
Contact **string
|
||||
Details **string
|
||||
Deadline **time.Time
|
||||
ActionTaken **string
|
||||
}
|
||||
)
|
||||
|
||||
func (crrr *CreateRightsRequestRequest) Validate() error {
|
||||
v := validator.New()
|
||||
|
||||
v.Check(crrr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
|
||||
v.Check(crrr.RequestType, "request_type", validator.Required(), validator.OneOfSlice(coredata.RightsRequestTypes()))
|
||||
v.Check(crrr.RequestState, "request_state", validator.Required(), validator.OneOfSlice(coredata.RightsRequestStates()))
|
||||
v.Check(crrr.DataSubject, "data_subject", validator.SafeText(ContentMaxLength))
|
||||
v.Check(crrr.Contact, "contact", validator.SafeText(ContentMaxLength))
|
||||
v.Check(crrr.Details, "details", validator.SafeText(ContentMaxLength))
|
||||
v.Check(crrr.ActionTaken, "action_taken", validator.SafeText(ContentMaxLength))
|
||||
|
||||
return v.Error()
|
||||
}
|
||||
|
||||
func (urrr *UpdateRightsRequestRequest) Validate() error {
|
||||
v := validator.New()
|
||||
|
||||
v.Check(urrr.ID, "id", validator.Required(), validator.GID(coredata.RightsRequestEntityType))
|
||||
v.Check(urrr.RequestType, "request_type", validator.OneOfSlice(coredata.RightsRequestTypes()))
|
||||
v.Check(urrr.RequestState, "request_state", validator.OneOfSlice(coredata.RightsRequestStates()))
|
||||
v.Check(urrr.DataSubject, "data_subject", validator.SafeText(ContentMaxLength))
|
||||
v.Check(urrr.Contact, "contact", validator.SafeText(ContentMaxLength))
|
||||
v.Check(urrr.Details, "details", validator.SafeText(ContentMaxLength))
|
||||
v.Check(urrr.ActionTaken, "action_taken", validator.SafeText(ContentMaxLength))
|
||||
|
||||
return v.Error()
|
||||
}
|
||||
|
||||
func (s RightsRequestService) Get(
|
||||
ctx context.Context,
|
||||
rightsRequestID gid.GID,
|
||||
) (*coredata.RightsRequest, error) {
|
||||
request := &coredata.RightsRequest{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := request.LoadByID(ctx, conn, s.svc.scope, rightsRequestID); err != nil {
|
||||
return fmt.Errorf("cannot load rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (s *RightsRequestService) Create(
|
||||
ctx context.Context,
|
||||
req *CreateRightsRequestRequest,
|
||||
) (*coredata.RightsRequest, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
request := &coredata.RightsRequest{
|
||||
ID: gid.New(s.svc.scope.GetTenantID(), coredata.RightsRequestEntityType),
|
||||
OrganizationID: req.OrganizationID,
|
||||
RequestType: *req.RequestType,
|
||||
RequestState: *req.RequestState,
|
||||
DataSubject: req.DataSubject,
|
||||
Contact: req.Contact,
|
||||
Details: req.Details,
|
||||
Deadline: req.Deadline,
|
||||
ActionTaken: req.ActionTaken,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
organization := &coredata.Organization{}
|
||||
if err := organization.LoadByID(ctx, conn, s.svc.scope, req.OrganizationID); err != nil {
|
||||
return fmt.Errorf("cannot load organization: %w", err)
|
||||
}
|
||||
|
||||
if err := request.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (s *RightsRequestService) Update(
|
||||
ctx context.Context,
|
||||
req *UpdateRightsRequestRequest,
|
||||
) (*coredata.RightsRequest, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &coredata.RightsRequest{}
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := request.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil {
|
||||
return fmt.Errorf("cannot load rights request: %w", err)
|
||||
}
|
||||
|
||||
if req.RequestType != nil {
|
||||
request.RequestType = *req.RequestType
|
||||
}
|
||||
|
||||
if req.RequestState != nil {
|
||||
request.RequestState = *req.RequestState
|
||||
}
|
||||
|
||||
if req.DataSubject != nil {
|
||||
request.DataSubject = *req.DataSubject
|
||||
}
|
||||
|
||||
if req.Contact != nil {
|
||||
request.Contact = *req.Contact
|
||||
}
|
||||
|
||||
if req.Details != nil {
|
||||
request.Details = *req.Details
|
||||
}
|
||||
|
||||
if req.Deadline != nil {
|
||||
request.Deadline = *req.Deadline
|
||||
}
|
||||
|
||||
if req.ActionTaken != nil {
|
||||
request.ActionTaken = *req.ActionTaken
|
||||
}
|
||||
|
||||
request.UpdatedAt = time.Now()
|
||||
|
||||
if err := request.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (s *RightsRequestService) Delete(
|
||||
ctx context.Context,
|
||||
rightsRequestID gid.GID,
|
||||
) error {
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
request := &coredata.RightsRequest{}
|
||||
if err := request.LoadByID(ctx, conn, s.svc.scope, rightsRequestID); err != nil {
|
||||
return fmt.Errorf("cannot load rights request: %w", err)
|
||||
}
|
||||
|
||||
if err := request.Delete(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot delete rights request: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s RightsRequestService) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
) (int, error) {
|
||||
var count int
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) (err error) {
|
||||
requests := coredata.RightsRequests{}
|
||||
count, err = requests.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot count rights requests: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s RightsRequestService) ListForOrganizationID(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
cursor *page.Cursor[coredata.RightsRequestOrderField],
|
||||
) (*page.Page[*coredata.RightsRequest, coredata.RightsRequestOrderField], error) {
|
||||
var requests coredata.RightsRequests
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := requests.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load rights requests: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage(requests, cursor), nil
|
||||
}
|
||||
@@ -113,6 +113,7 @@ type (
|
||||
Obligations *ObligationService
|
||||
Snapshots *SnapshotService
|
||||
ContinualImprovements *ContinualImprovementService
|
||||
RightsRequests *RightsRequestService
|
||||
ProcessingActivities *ProcessingActivityService
|
||||
DataProtectionImpactAssessments *DataProtectionImpactAssessmentService
|
||||
TransferImpactAssessments *TransferImpactAssessmentService
|
||||
@@ -250,6 +251,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
|
||||
tenantService.Obligations = &ObligationService{svc: tenantService}
|
||||
tenantService.Snapshots = &SnapshotService{svc: tenantService}
|
||||
tenantService.ContinualImprovements = &ContinualImprovementService{svc: tenantService}
|
||||
tenantService.RightsRequests = &RightsRequestService{svc: tenantService}
|
||||
tenantService.ProcessingActivities = &ProcessingActivityService{svc: tenantService}
|
||||
tenantService.DataProtectionImpactAssessments = &DataProtectionImpactAssessmentService{svc: tenantService}
|
||||
tenantService.TransferImpactAssessments = &TransferImpactAssessmentService{svc: tenantService}
|
||||
|
||||
@@ -260,6 +260,42 @@ enum ContinualImprovementPriority
|
||||
)
|
||||
}
|
||||
|
||||
enum RightsRequestType
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.RightsRequestType"
|
||||
) {
|
||||
ACCESS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeAccess"
|
||||
)
|
||||
DELETION
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeDeletion"
|
||||
)
|
||||
PORTABILITY
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypePortability"
|
||||
)
|
||||
}
|
||||
|
||||
enum RightsRequestState
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.RightsRequestState"
|
||||
) {
|
||||
TODO
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateTodo"
|
||||
)
|
||||
IN_PROGRESS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateInProgress"
|
||||
)
|
||||
DONE
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateDone"
|
||||
)
|
||||
}
|
||||
|
||||
enum ProcessingActivitySpecialOrCriminalDatum
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivitySpecialOrCriminalDatum"
|
||||
@@ -1065,6 +1101,28 @@ enum ContinualImprovementOrderField
|
||||
)
|
||||
}
|
||||
|
||||
enum RightsRequestOrderField
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderField"
|
||||
) {
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldCreatedAt"
|
||||
)
|
||||
DEADLINE
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldDeadline"
|
||||
)
|
||||
STATE
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldState"
|
||||
)
|
||||
TYPE
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldType"
|
||||
)
|
||||
}
|
||||
|
||||
enum ProcessingActivityOrderField
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityOrderField"
|
||||
@@ -1344,6 +1402,14 @@ input ContinualImprovementOrder
|
||||
field: ContinualImprovementOrderField!
|
||||
}
|
||||
|
||||
input RightsRequestOrder
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.RightsRequestOrderBy"
|
||||
) {
|
||||
direction: OrderDirection!
|
||||
field: RightsRequestOrderField!
|
||||
}
|
||||
|
||||
input ProcessingActivityOrder
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ProcessingActivityOrderBy"
|
||||
@@ -1513,6 +1579,7 @@ input ContinualImprovementFilter {
|
||||
snapshotId: ID
|
||||
}
|
||||
|
||||
|
||||
input ProcessingActivityFilter {
|
||||
snapshotId: ID
|
||||
}
|
||||
@@ -1727,6 +1794,14 @@ type Organization implements Node {
|
||||
filter: ContinualImprovementFilter = { snapshotId: null }
|
||||
): ContinualImprovementConnection! @goField(forceResolver: true)
|
||||
|
||||
rightsRequests(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: RightsRequestOrder
|
||||
): RightsRequestConnection! @goField(forceResolver: true)
|
||||
|
||||
processingActivities(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
@@ -2326,6 +2401,20 @@ type ContinualImprovement implements Node {
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type RightsRequest implements Node {
|
||||
id: ID!
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
requestType: RightsRequestType!
|
||||
requestState: RightsRequestState!
|
||||
dataSubject: String
|
||||
contact: String
|
||||
details: String
|
||||
deadline: Datetime
|
||||
actionTaken: String
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type ProcessingActivity implements Node {
|
||||
id: ID!
|
||||
snapshotId: ID
|
||||
@@ -2879,6 +2968,20 @@ type ContinualImprovementEdge {
|
||||
node: ContinualImprovement!
|
||||
}
|
||||
|
||||
type RightsRequestConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.RightsRequestConnection"
|
||||
) {
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
edges: [RightsRequestEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type RightsRequestEdge {
|
||||
cursor: CursorKey!
|
||||
node: RightsRequest!
|
||||
}
|
||||
|
||||
type ProcessingActivityConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ProcessingActivityConnection"
|
||||
@@ -3246,6 +3349,20 @@ type Mutation {
|
||||
deleteContinualImprovement(
|
||||
input: DeleteContinualImprovementInput!
|
||||
): DeleteContinualImprovementPayload!
|
||||
|
||||
# Rights Request mutations
|
||||
createRightsRequest(
|
||||
input: CreateRightsRequestInput!
|
||||
): CreateRightsRequestPayload!
|
||||
|
||||
updateRightsRequest(
|
||||
input: UpdateRightsRequestInput!
|
||||
): UpdateRightsRequestPayload!
|
||||
|
||||
deleteRightsRequest(
|
||||
input: DeleteRightsRequestInput!
|
||||
): DeleteRightsRequestPayload!
|
||||
|
||||
# Processing Activity mutations
|
||||
createProcessingActivity(
|
||||
input: CreateProcessingActivityInput!
|
||||
@@ -4022,6 +4139,32 @@ input DeleteContinualImprovementInput {
|
||||
continualImprovementId: ID!
|
||||
}
|
||||
|
||||
input CreateRightsRequestInput {
|
||||
organizationId: ID!
|
||||
requestType: RightsRequestType!
|
||||
requestState: RightsRequestState!
|
||||
dataSubject: String
|
||||
contact: String
|
||||
details: String
|
||||
deadline: Datetime
|
||||
actionTaken: String
|
||||
}
|
||||
|
||||
input UpdateRightsRequestInput {
|
||||
id: ID!
|
||||
requestType: RightsRequestType
|
||||
requestState: RightsRequestState
|
||||
dataSubject: String @goField(omittable: true)
|
||||
contact: String @goField(omittable: true)
|
||||
details: String @goField(omittable: true)
|
||||
deadline: Datetime @goField(omittable: true)
|
||||
actionTaken: String @goField(omittable: true)
|
||||
}
|
||||
|
||||
input DeleteRightsRequestInput {
|
||||
rightsRequestId: ID!
|
||||
}
|
||||
|
||||
input CreateProcessingActivityInput {
|
||||
organizationId: ID!
|
||||
name: String!
|
||||
@@ -4964,6 +5107,18 @@ type DeleteContinualImprovementPayload {
|
||||
deletedContinualImprovementId: ID!
|
||||
}
|
||||
|
||||
type CreateRightsRequestPayload {
|
||||
rightsRequestEdge: RightsRequestEdge!
|
||||
}
|
||||
|
||||
type UpdateRightsRequestPayload {
|
||||
rightsRequest: RightsRequest!
|
||||
}
|
||||
|
||||
type DeleteRightsRequestPayload {
|
||||
deletedRightsRequestId: ID!
|
||||
}
|
||||
|
||||
type CreateProcessingActivityPayload {
|
||||
processingActivityEdge: ProcessingActivityEdge!
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
75
pkg/server/api/console/v1/types/rights_request.go
Normal file
75
pkg/server/api/console/v1/types/rights_request.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
RightsRequestOrderBy OrderBy[coredata.RightsRequestOrderField]
|
||||
|
||||
RightsRequestConnection struct {
|
||||
TotalCount int
|
||||
Edges []*RightsRequestEdge
|
||||
PageInfo PageInfo
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
}
|
||||
)
|
||||
|
||||
func NewRightsRequestConnection(
|
||||
p *page.Page[*coredata.RightsRequest, coredata.RightsRequestOrderField],
|
||||
parentType any,
|
||||
parentID gid.GID,
|
||||
) *RightsRequestConnection {
|
||||
edges := make([]*RightsRequestEdge, len(p.Data))
|
||||
for i, request := range p.Data {
|
||||
edges[i] = NewRightsRequestEdge(request, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &RightsRequestConnection{
|
||||
Edges: edges,
|
||||
PageInfo: *NewPageInfo(p),
|
||||
|
||||
Resolver: parentType,
|
||||
ParentID: parentID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRightsRequest(rr *coredata.RightsRequest) *RightsRequest {
|
||||
return &RightsRequest{
|
||||
ID: rr.ID,
|
||||
RequestType: rr.RequestType,
|
||||
RequestState: rr.RequestState,
|
||||
DataSubject: rr.DataSubject,
|
||||
Contact: rr.Contact,
|
||||
Details: rr.Details,
|
||||
Deadline: rr.Deadline,
|
||||
ActionTaken: rr.ActionTaken,
|
||||
CreatedAt: rr.CreatedAt,
|
||||
UpdatedAt: rr.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRightsRequestEdge(rr *coredata.RightsRequest, orderField coredata.RightsRequestOrderField) *RightsRequestEdge {
|
||||
return &RightsRequestEdge{
|
||||
Node: NewRightsRequest(rr),
|
||||
Cursor: rr.CursorKey(orderField),
|
||||
}
|
||||
}
|
||||
@@ -464,6 +464,21 @@ type CreateProcessingActivityPayload struct {
|
||||
ProcessingActivityEdge *ProcessingActivityEdge `json:"processingActivityEdge"`
|
||||
}
|
||||
|
||||
type CreateRightsRequestInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
RequestType coredata.RightsRequestType `json:"requestType"`
|
||||
RequestState coredata.RightsRequestState `json:"requestState"`
|
||||
DataSubject *string `json:"dataSubject,omitempty"`
|
||||
Contact *string `json:"contact,omitempty"`
|
||||
Details *string `json:"details,omitempty"`
|
||||
Deadline *time.Time `json:"deadline,omitempty"`
|
||||
ActionTaken *string `json:"actionTaken,omitempty"`
|
||||
}
|
||||
|
||||
type CreateRightsRequestPayload struct {
|
||||
RightsRequestEdge *RightsRequestEdge `json:"rightsRequestEdge"`
|
||||
}
|
||||
|
||||
type CreateRiskDocumentMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
DocumentID gid.GID `json:"documentId"`
|
||||
@@ -934,6 +949,14 @@ type DeleteProcessingActivityPayload struct {
|
||||
DeletedProcessingActivityID gid.GID `json:"deletedProcessingActivityId"`
|
||||
}
|
||||
|
||||
type DeleteRightsRequestInput struct {
|
||||
RightsRequestID gid.GID `json:"rightsRequestId"`
|
||||
}
|
||||
|
||||
type DeleteRightsRequestPayload struct {
|
||||
DeletedRightsRequestID gid.GID `json:"deletedRightsRequestId"`
|
||||
}
|
||||
|
||||
type DeleteRiskDocumentMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
DocumentID gid.GID `json:"documentId"`
|
||||
@@ -1515,6 +1538,7 @@ type Organization struct {
|
||||
Nonconformities *NonconformityConnection `json:"nonconformities"`
|
||||
Obligations *ObligationConnection `json:"obligations"`
|
||||
ContinualImprovements *ContinualImprovementConnection `json:"continualImprovements"`
|
||||
RightsRequests *RightsRequestConnection `json:"rightsRequests"`
|
||||
ProcessingActivities *ProcessingActivityConnection `json:"processingActivities"`
|
||||
DataProtectionImpactAssessments *DataProtectionImpactAssessmentConnection `json:"dataProtectionImpactAssessments"`
|
||||
TransferImpactAssessments *TransferImpactAssessmentConnection `json:"transferImpactAssessments"`
|
||||
@@ -1682,6 +1706,28 @@ type RequestSignaturePayload struct {
|
||||
DocumentVersionSignatureEdge *DocumentVersionSignatureEdge `json:"documentVersionSignatureEdge"`
|
||||
}
|
||||
|
||||
type RightsRequest struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Organization *Organization `json:"organization"`
|
||||
RequestType coredata.RightsRequestType `json:"requestType"`
|
||||
RequestState coredata.RightsRequestState `json:"requestState"`
|
||||
DataSubject *string `json:"dataSubject,omitempty"`
|
||||
Contact *string `json:"contact,omitempty"`
|
||||
Details *string `json:"details,omitempty"`
|
||||
Deadline *time.Time `json:"deadline,omitempty"`
|
||||
ActionTaken *string `json:"actionTaken,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (RightsRequest) IsNode() {}
|
||||
func (this RightsRequest) GetID() gid.GID { return this.ID }
|
||||
|
||||
type RightsRequestEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *RightsRequest `json:"node"`
|
||||
}
|
||||
|
||||
type Risk struct {
|
||||
ID gid.GID `json:"id"`
|
||||
SnapshotID *gid.GID `json:"snapshotId,omitempty"`
|
||||
@@ -2199,6 +2245,21 @@ type UpdateProcessingActivityPayload struct {
|
||||
ProcessingActivity *ProcessingActivity `json:"processingActivity"`
|
||||
}
|
||||
|
||||
type UpdateRightsRequestInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
RequestType *coredata.RightsRequestType `json:"requestType,omitempty"`
|
||||
RequestState *coredata.RightsRequestState `json:"requestState,omitempty"`
|
||||
DataSubject graphql.Omittable[*string] `json:"dataSubject,omitempty"`
|
||||
Contact graphql.Omittable[*string] `json:"contact,omitempty"`
|
||||
Details graphql.Omittable[*string] `json:"details,omitempty"`
|
||||
Deadline graphql.Omittable[*time.Time] `json:"deadline,omitempty"`
|
||||
ActionTaken graphql.Omittable[*string] `json:"actionTaken,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateRightsRequestPayload struct {
|
||||
RightsRequest *RightsRequest `json:"rightsRequest"`
|
||||
}
|
||||
|
||||
type UpdateRiskInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
|
||||
@@ -4243,6 +4243,76 @@ func (r *mutationResolver) DeleteContinualImprovement(ctx context.Context, input
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateRightsRequest is the resolver for the createRightsRequest field.
|
||||
func (r *mutationResolver) CreateRightsRequest(ctx context.Context, input types.CreateRightsRequestInput) (*types.CreateRightsRequestPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionCreateRightsRequest)
|
||||
|
||||
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
req := probo.CreateRightsRequestRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
RequestType: &input.RequestType,
|
||||
RequestState: &input.RequestState,
|
||||
DataSubject: input.DataSubject,
|
||||
Contact: input.Contact,
|
||||
Details: input.Details,
|
||||
Deadline: input.Deadline,
|
||||
ActionTaken: input.ActionTaken,
|
||||
}
|
||||
|
||||
rightsRequest, err := prb.RightsRequests.Create(ctx, &req)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create rights request: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateRightsRequestPayload{
|
||||
RightsRequestEdge: types.NewRightsRequestEdge(rightsRequest, coredata.RightsRequestOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateRightsRequest is the resolver for the updateRightsRequest field.
|
||||
func (r *mutationResolver) UpdateRightsRequest(ctx context.Context, input types.UpdateRightsRequestInput) (*types.UpdateRightsRequestPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.ID, authz.ActionUpdateRightsRequest)
|
||||
|
||||
prb := r.ProboService(ctx, input.ID.TenantID())
|
||||
|
||||
req := probo.UpdateRightsRequestRequest{
|
||||
ID: input.ID,
|
||||
RequestType: input.RequestType,
|
||||
RequestState: input.RequestState,
|
||||
DataSubject: UnwrapOmittable(input.DataSubject),
|
||||
Contact: UnwrapOmittable(input.Contact),
|
||||
Details: UnwrapOmittable(input.Details),
|
||||
Deadline: UnwrapOmittable(input.Deadline),
|
||||
ActionTaken: UnwrapOmittable(input.ActionTaken),
|
||||
}
|
||||
|
||||
rightsRequest, err := prb.RightsRequests.Update(ctx, &req)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot update rights request: %w", err))
|
||||
}
|
||||
|
||||
return &types.UpdateRightsRequestPayload{
|
||||
RightsRequest: types.NewRightsRequest(rightsRequest),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteRightsRequest is the resolver for the deleteRightsRequest field.
|
||||
func (r *mutationResolver) DeleteRightsRequest(ctx context.Context, input types.DeleteRightsRequestInput) (*types.DeleteRightsRequestPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.RightsRequestID, authz.ActionDeleteRightsRequest)
|
||||
|
||||
prb := r.ProboService(ctx, input.RightsRequestID.TenantID())
|
||||
|
||||
err := prb.RightsRequests.Delete(ctx, input.RightsRequestID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot delete rights request: %w", err))
|
||||
}
|
||||
|
||||
return &types.DeleteRightsRequestPayload{
|
||||
DeletedRightsRequestID: input.RightsRequestID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateProcessingActivity is the resolver for the createProcessingActivity field.
|
||||
func (r *mutationResolver) CreateProcessingActivity(ctx context.Context, input types.CreateProcessingActivityInput) (*types.CreateProcessingActivityPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionCreateProcessingActivity)
|
||||
@@ -5535,6 +5605,34 @@ func (r *organizationResolver) ContinualImprovements(ctx context.Context, obj *t
|
||||
return types.NewContinualImprovementConnection(page, r, obj.ID, filter), nil
|
||||
}
|
||||
|
||||
// RightsRequests is the resolver for the rightsRequests field.
|
||||
func (r *organizationResolver) RightsRequests(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RightsRequestOrderBy) (*types.RightsRequestConnection, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionListRightsRequests)
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.RightsRequestOrderField]{
|
||||
Field: coredata.RightsRequestOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.RightsRequestOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := prb.RightsRequests.ListForOrganizationID(ctx, obj.ID, cursor)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list organization rights requests: %w", err))
|
||||
}
|
||||
|
||||
return types.NewRightsRequestConnection(page, r, obj.ID), nil
|
||||
}
|
||||
|
||||
// ProcessingActivities is the resolver for the processingActivities field.
|
||||
func (r *organizationResolver) ProcessingActivities(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProcessingActivityOrderBy, filter *types.ProcessingActivityFilter) (*types.ProcessingActivityConnection, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionListProcessingActivities)
|
||||
@@ -6146,6 +6244,17 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
}
|
||||
|
||||
return types.NewMeeting(meeting), nil
|
||||
case coredata.RightsRequestEntityType:
|
||||
rightsRequest, err := prb.RightsRequests.Get(ctx, id)
|
||||
if err != nil {
|
||||
var errNotFound *coredata.ErrRightsRequestNotFound
|
||||
if errors.As(err, &errNotFound) {
|
||||
return nil, gqlutils.NotFound(errNotFound)
|
||||
}
|
||||
panic(fmt.Errorf("cannot get rights request: %w", err))
|
||||
}
|
||||
|
||||
return types.NewRightsRequest(rightsRequest), nil
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -6201,6 +6310,48 @@ func (r *reportResolver) Audit(ctx context.Context, obj *types.Report) (*types.A
|
||||
return types.NewAudit(audit), nil
|
||||
}
|
||||
|
||||
// Organization is the resolver for the organization field.
|
||||
func (r *rightsRequestResolver) Organization(ctx context.Context, obj *types.RightsRequest) (*types.Organization, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetOrganization)
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
rightsRequest, err := prb.RightsRequests.Get(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get rights request: %w", err))
|
||||
}
|
||||
|
||||
organization, err := prb.Organizations.Get(ctx, rightsRequest.OrganizationID)
|
||||
if err != nil {
|
||||
var errNotFound *coredata.ErrOrganizationNotFound
|
||||
if errors.As(err, &errNotFound) {
|
||||
return nil, gqlutils.NotFound(errNotFound)
|
||||
}
|
||||
panic(fmt.Errorf("cannot get organization: %w", err))
|
||||
}
|
||||
|
||||
return types.NewOrganization(organization), nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
func (r *rightsRequestConnectionResolver) TotalCount(ctx context.Context, obj *types.RightsRequestConnection) (int, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ParentID, authz.ActionTotalCount)
|
||||
|
||||
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
||||
|
||||
switch obj.Resolver.(type) {
|
||||
case *organizationResolver:
|
||||
count, err := prb.RightsRequests.CountByOrganizationID(ctx, obj.ParentID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot count rights requests: %w", err))
|
||||
}
|
||||
|
||||
return count, nil
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported resolver type for RightsRequestConnection: %T", obj.Resolver))
|
||||
}
|
||||
}
|
||||
|
||||
// Owner is the resolver for the owner field.
|
||||
func (r *riskResolver) Owner(ctx context.Context, obj *types.Risk) (*types.People, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetOwner)
|
||||
@@ -7789,6 +7940,14 @@ func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
|
||||
// Report returns schema.ReportResolver implementation.
|
||||
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
|
||||
|
||||
// RightsRequest returns schema.RightsRequestResolver implementation.
|
||||
func (r *Resolver) RightsRequest() schema.RightsRequestResolver { return &rightsRequestResolver{r} }
|
||||
|
||||
// RightsRequestConnection returns schema.RightsRequestConnectionResolver implementation.
|
||||
func (r *Resolver) RightsRequestConnection() schema.RightsRequestConnectionResolver {
|
||||
return &rightsRequestConnectionResolver{r}
|
||||
}
|
||||
|
||||
// Risk returns schema.RiskResolver implementation.
|
||||
func (r *Resolver) Risk() schema.RiskResolver { return &riskResolver{r} }
|
||||
|
||||
@@ -7947,6 +8106,8 @@ type processingActivityResolver struct{ *Resolver }
|
||||
type processingActivityConnectionResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type reportResolver struct{ *Resolver }
|
||||
type rightsRequestResolver struct{ *Resolver }
|
||||
type rightsRequestConnectionResolver struct{ *Resolver }
|
||||
type riskResolver struct{ *Resolver }
|
||||
type riskConnectionResolver struct{ *Resolver }
|
||||
type sAMLConfigurationResolver struct{ *Resolver }
|
||||
|
||||
Reference in New Issue
Block a user