From 581f9b60d651d9f2a1b68dfa809e0d4f874c9d07 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 29 Aug 2024 13:57:37 -0500 Subject: [PATCH 01/56] Restart migration chain to ease db load (#3282) --- dbschema/migrations/00001-m13xep3.edgeql | 1934 ---------------------- dbschema/migrations/00001-m1zafss.edgeql | 708 ++++++++ dbschema/migrations/00002-m1jtglk.edgeql | 13 - dbschema/migrations/00002-m1oatla.edgeql | 581 +++++++ dbschema/migrations/00003-m1nrdwc.edgeql | 52 - dbschema/migrations/00003-m1zasar.edgeql | 64 + dbschema/migrations/00004-m1pqnnd.edgeql | 29 - dbschema/migrations/00004-m1yphrx.edgeql | 343 ++++ dbschema/migrations/00005-m1jmb5p.edgeql | 8 - dbschema/migrations/00006-m1rxhi7.edgeql | 687 -------- dbschema/migrations/00007-m15wwix.edgeql | 11 - dbschema/migrations/00008-m1pgopp.edgeql | 17 - dbschema/migrations/00009-m1bzq66.edgeql | 11 - dbschema/migrations/00010-m1sznh4.edgeql | 70 - dbschema/migrations/00011-m1ohpds.edgeql | 24 - dbschema/migrations/00012-m1ah2np.edgeql | 9 - dbschema/migrations/00013-m1wi5kt.edgeql | 13 - 17 files changed, 1696 insertions(+), 2878 deletions(-) delete mode 100644 dbschema/migrations/00001-m13xep3.edgeql create mode 100644 dbschema/migrations/00001-m1zafss.edgeql delete mode 100644 dbschema/migrations/00002-m1jtglk.edgeql create mode 100644 dbschema/migrations/00002-m1oatla.edgeql delete mode 100644 dbschema/migrations/00003-m1nrdwc.edgeql create mode 100644 dbschema/migrations/00003-m1zasar.edgeql delete mode 100644 dbschema/migrations/00004-m1pqnnd.edgeql create mode 100644 dbschema/migrations/00004-m1yphrx.edgeql delete mode 100644 dbschema/migrations/00005-m1jmb5p.edgeql delete mode 100644 dbschema/migrations/00006-m1rxhi7.edgeql delete mode 100644 dbschema/migrations/00007-m15wwix.edgeql delete mode 100644 dbschema/migrations/00008-m1pgopp.edgeql delete mode 100644 dbschema/migrations/00009-m1bzq66.edgeql delete mode 100644 dbschema/migrations/00010-m1sznh4.edgeql delete mode 100644 dbschema/migrations/00011-m1ohpds.edgeql delete mode 100644 dbschema/migrations/00012-m1ah2np.edgeql delete mode 100644 dbschema/migrations/00013-m1wi5kt.edgeql diff --git a/dbschema/migrations/00001-m13xep3.edgeql b/dbschema/migrations/00001-m13xep3.edgeql deleted file mode 100644 index 2ecd76342f..0000000000 --- a/dbschema/migrations/00001-m13xep3.edgeql +++ /dev/null @@ -1,1934 +0,0 @@ -CREATE MIGRATION m13xep3dlmh5po2lifdg2sqasl556k5zfzyeyj7j2pc4u4z5rlouia - ONTO initial -{ - CREATE MODULE Auth IF NOT EXISTS; - CREATE MODULE Budget IF NOT EXISTS; - CREATE MODULE Comments IF NOT EXISTS; - CREATE MODULE Engagement IF NOT EXISTS; - CREATE MODULE Ethnologue IF NOT EXISTS; - CREATE MODULE File IF NOT EXISTS; - CREATE MODULE Location IF NOT EXISTS; - CREATE MODULE Media IF NOT EXISTS; - CREATE MODULE Mixin IF NOT EXISTS; - CREATE MODULE Organization IF NOT EXISTS; - CREATE MODULE Partner IF NOT EXISTS; - CREATE MODULE Partnership IF NOT EXISTS; - CREATE MODULE Post IF NOT EXISTS; - CREATE MODULE Product IF NOT EXISTS; - CREATE MODULE ProgressReport IF NOT EXISTS; - CREATE MODULE ProgressReport::Media IF NOT EXISTS; - CREATE MODULE ProgressReport::ProductProgress IF NOT EXISTS; - CREATE MODULE Project IF NOT EXISTS; - CREATE MODULE Prompt IF NOT EXISTS; - CREATE MODULE Scripture IF NOT EXISTS; - CREATE MODULE User IF NOT EXISTS; - CREATE GLOBAL default::currentUserId -> std::uuid; - CREATE ABSTRACT TYPE Mixin::Timestamped { - CREATE REQUIRED PROPERTY createdAt: std::datetime { - SET default := (std::datetime_of_statement()); - SET readonly := true; - }; - CREATE REQUIRED PROPERTY modifiedAt: std::datetime { - SET default := (std::datetime_of_statement()); - CREATE REWRITE - UPDATE - USING (std::datetime_of_statement()); - }; - }; - CREATE ABSTRACT TYPE default::Resource EXTENDING Mixin::Timestamped; - CREATE SCALAR TYPE User::Degree EXTENDING enum; - CREATE TYPE User::Education EXTENDING default::Resource { - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEducation - ALLOW UPDATE WRITE ; - CREATE REQUIRED PROPERTY degree: User::Degree; - CREATE REQUIRED PROPERTY institution: std::str; - CREATE REQUIRED PROPERTY major: std::str; - }; - CREATE TYPE User::Unavailability EXTENDING default::Resource { - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForUnavailability - ALLOW UPDATE WRITE ; - CREATE REQUIRED PROPERTY dates: range; - CREATE REQUIRED PROPERTY description: std::str; - }; - CREATE SCALAR TYPE User::Status EXTENDING enum; - CREATE SCALAR TYPE default::Role EXTENDING enum; - CREATE ABSTRACT TYPE Mixin::Pinnable; - CREATE TYPE default::User EXTENDING default::Resource, Mixin::Pinnable { - CREATE MULTI LINK pins: Mixin::Pinnable { - ON TARGET DELETE ALLOW; - }; - CREATE MULTI LINK education: User::Education { - ON TARGET DELETE ALLOW; - }; - CREATE MULTI LINK unavailabilities: User::Unavailability { - ON TARGET DELETE ALLOW; - }; - CREATE PROPERTY about: std::str; - CREATE REQUIRED PROPERTY realFirstName: std::str; - CREATE REQUIRED PROPERTY displayFirstName: std::str { - SET default := (.realFirstName); - }; - CREATE REQUIRED PROPERTY realLastName: std::str; - CREATE REQUIRED PROPERTY displayLastName: std::str { - SET default := (.realLastName); - }; - CREATE PROPERTY email: std::str { - CREATE CONSTRAINT std::exclusive; - }; - CREATE PROPERTY phone: std::str; - CREATE MULTI PROPERTY roles: default::Role; - CREATE REQUIRED PROPERTY status: User::Status { - SET default := (User::Status.Active); - }; - CREATE REQUIRED PROPERTY timezone: std::str { - SET default := 'America/Chicago'; - }; - CREATE PROPERTY title: std::str; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForUser - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForUser - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForUser - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT givenRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'}))) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForUser - ALLOW UPDATE WRITE ; - }; - ALTER TYPE Mixin::Pinnable { - CREATE PROPERTY pinned := ((__source__ IN (GLOBAL default::currentUserId).pins)); - }; - CREATE ALIAS default::currentUser := ( - GLOBAL default::currentUserId - ); - CREATE SCALAR TYPE default::RichText EXTENDING std::json; - CREATE ABSTRACT TYPE Mixin::Owned { - CREATE LINK owner: default::User { - SET default := (default::currentUser); - }; - CREATE PROPERTY isOwner := ((.owner = GLOBAL default::currentUserId)); - }; - CREATE TYPE Comments::Comment EXTENDING default::Resource, Mixin::Owned { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForComment - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (.isOwner ?? false)) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForComment - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForComment - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) OR (.isOwner ?? false)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForComment - ALLOW UPDATE WRITE ; - CREATE REQUIRED PROPERTY body: default::RichText; - }; - CREATE ABSTRACT TYPE Mixin::Embedded { - CREATE REQUIRED SINGLE LINK container: default::Resource; - }; - CREATE TYPE Comments::Thread EXTENDING default::Resource, Mixin::Embedded, Mixin::Owned { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForCommentThread - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (.isOwner ?? false)) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForCommentThread - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCommentThread - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) OR (.isOwner ?? false)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForCommentThread - ALLOW UPDATE WRITE ; - }; - CREATE ABSTRACT TYPE Mixin::Named { - CREATE REQUIRED PROPERTY name: std::str; - CREATE INDEX fts::index ON (fts::with_options(.name, language := fts::Language.eng)); - }; - CREATE ABSTRACT TYPE File::Node EXTENDING default::Resource, Mixin::Named { - CREATE REQUIRED LINK createdBy: default::User { - SET default := (default::currentUser); - }; - CREATE REQUIRED LINK modifiedBy: default::User { - SET default := (default::currentUser); - CREATE REWRITE - UPDATE - USING (default::currentUser); - }; - CREATE LINK parent: File::Node; - CREATE MULTI LINK parents: File::Node { - CREATE PROPERTY depth: std::int16; - }; - CREATE PROPERTY public: std::bool; - CREATE REQUIRED PROPERTY size: std::int64; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFileNode - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFileNode - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFileNode - ALLOW UPDATE WRITE ; - }; - CREATE TYPE File::Version EXTENDING File::Node { - CREATE REQUIRED PROPERTY mimeType: std::str; - }; - CREATE TYPE default::Directory EXTENDING File::Node { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForDirectory - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) - ); - CREATE REQUIRED PROPERTY totalFiles: std::int32 { - SET default := 0; - }; - }; - CREATE ABSTRACT TYPE default::Media { - CREATE REQUIRED LINK file: File::Version { - SET readonly := true; - CREATE CONSTRAINT std::exclusive; - }; - CREATE PROPERTY altText: std::str; - CREATE PROPERTY caption: std::str; - CREATE REQUIRED PROPERTY mimeType: std::str; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForMedia - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForMedia - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForMedia - ALLOW UPDATE WRITE ; - }; - CREATE TYPE default::File EXTENDING File::Node { - CREATE REQUIRED LINK latestVersion: File::Version; - CREATE SINGLE LINK media := (.latestVersion.; - CREATE SCALAR TYPE Project::Step EXTENDING enum; - CREATE FUNCTION Project::statusFromStep(step: Project::Step) -> Project::Status USING (WITH - dev := - {Project::Step.EarlyConversations, Project::Step.PendingConceptApproval, Project::Step.PrepForConsultantEndorsement, Project::Step.PendingConsultantEndorsement, Project::Step.PrepForFinancialEndorsement, Project::Step.PendingFinancialEndorsement, Project::Step.FinalizingProposal, Project::Step.PendingRegionalDirectorApproval, Project::Step.PendingZoneDirectorApproval, Project::Step.PendingFinanceConfirmation, Project::Step.OnHoldFinanceConfirmation} - , - active := - {Project::Step.Active, Project::Step.ActiveChangedPlan, Project::Step.DiscussingChangeToPlan, Project::Step.PendingChangeToPlanApproval, Project::Step.PendingChangeToPlanConfirmation, Project::Step.DiscussingSuspension, Project::Step.PendingSuspensionApproval, Project::Step.Suspended, Project::Step.DiscussingReactivation, Project::Step.PendingReactivationApproval, Project::Step.DiscussingTermination, Project::Step.PendingTerminationApproval, Project::Step.FinalizingCompletion} - SELECT - (Project::Status.InDevelopment IF (step IN dev) ELSE (Project::Status.Active IF (step IN active) ELSE (Project::Status.DidNotDevelop IF (step = Project::Step.DidNotDevelop) ELSE (Project::Status.DidNotDevelop IF (step = Project::Step.Rejected) ELSE (Project::Status.Terminated IF (step = Project::Step.Terminated) ELSE (Project::Status.Completed IF (step = Project::Step.Completed) ELSE Project::Status.InDevelopment)))))) - ); - CREATE FUNCTION default::date_range_get_upper(period: range) -> cal::local_date USING ((std::assert_exists(std::range_get_upper(period)) - '1 day')); - CREATE SCALAR TYPE Post::Shareability EXTENDING enum; - CREATE SCALAR TYPE Post::Type EXTENDING enum; - CREATE SCALAR TYPE default::Sensitivity EXTENDING enum; - CREATE TYPE default::Post EXTENDING default::Resource, Mixin::Embedded, Mixin::Owned { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForPost - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (.isOwner ?? false)) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForPost - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPost - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) OR (.isOwner ?? false)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPost - ALLOW UPDATE WRITE ; - CREATE REQUIRED PROPERTY body: default::RichText; - CREATE REQUIRED PROPERTY shareability: Post::Shareability; - CREATE REQUIRED PROPERTY type: Post::Type; - }; - CREATE SCALAR TYPE default::nanoid EXTENDING std::str; - CREATE ABSTRACT TYPE Project::ContextAware { - CREATE OPTIONAL PROPERTY ownSensitivity: default::Sensitivity { - CREATE ANNOTATION std::description := "A writable source of a sensitivity. This doesn't necessarily mean it be the same as .sensitivity, which is what is used for authorization."; - }; - CREATE ANNOTATION std::description := 'A type that has a project context, which allows it to be\n aware of the sensitivity & current user membership for the associated context.'; - }; - CREATE ABSTRACT TYPE Project::Child EXTENDING default::Resource, Project::ContextAware { - CREATE ANNOTATION std::description := 'A type that is a child of a project. It will always have a reference to a single project that it is under.'; - }; - CREATE ABSTRACT TYPE Engagement::Child EXTENDING Project::Child { - CREATE ANNOTATION std::description := 'A type that is a child of an engagement. It will always have a reference to a single engagement & project that it is under.'; - }; - CREATE ABSTRACT TYPE ProgressReport::Child EXTENDING Engagement::Child { - CREATE ANNOTATION std::description := 'A type that is a child of a progress report. It will always have a reference to a single progress report and engagement that it is under.'; - }; - CREATE ABSTRACT TYPE Prompt::PromptVariantResponse EXTENDING Mixin::Embedded, Mixin::Timestamped, Mixin::Owned { - CREATE PROPERTY promptId: default::nanoid; - CREATE ANNOTATION std::description := 'An instance of a prompt and the responses per variant.'; - }; - CREATE TYPE ProgressReport::CommunityStory EXTENDING ProgressReport::Child, Prompt::PromptVariantResponse { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW UPDATE WRITE ; - }; - CREATE TYPE ProgressReport::Media::VariantGroup; - CREATE TYPE ProgressReport::Media EXTENDING ProgressReport::Child, Mixin::Owned { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportMedia - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (.isOwner ?? false)) - ); - CREATE REQUIRED PROPERTY variant: std::str; - CREATE REQUIRED LINK file: default::File; - CREATE REQUIRED SINGLE LINK media := (std::assert_exists(.file.media)); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportMedia - ALLOW UPDATE WRITE ; - CREATE REQUIRED LINK variantGroup: ProgressReport::Media::VariantGroup; - CREATE CONSTRAINT std::exclusive ON ((.variantGroup, .variant)); - CREATE TRIGGER deleteEmptyVariantGroup - AFTER DELETE - FOR EACH DO (DELETE - __old__.variantGroup - FILTER - NOT (EXISTS ((SELECT - ProgressReport::Media - FILTER - (.variantGroup = __old__.variantGroup) - ))) - ); - CREATE PROPERTY category: std::str; - }; - CREATE TYPE ProgressReport::TeamNews EXTENDING ProgressReport::Child, Prompt::PromptVariantResponse { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW UPDATE WRITE ; - }; - CREATE TYPE ProgressReport::Highlight EXTENDING ProgressReport::Child, Prompt::PromptVariantResponse { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW UPDATE WRITE ; - }; - CREATE TYPE Prompt::VariantResponse EXTENDING Mixin::Timestamped, Mixin::Owned { - CREATE REQUIRED LINK pvr: Prompt::PromptVariantResponse; - CREATE REQUIRED PROPERTY variant: std::str; - CREATE CONSTRAINT std::exclusive ON ((.pvr, .variant)); - CREATE ANNOTATION std::description := 'A response (for a variant) to an instance of a prompt.'; - CREATE PROPERTY response: default::RichText; - }; - CREATE SCALAR TYPE ProgressReport::Status EXTENDING enum; - CREATE TYPE ProgressReport::WorkflowEvent { - CREATE REQUIRED LINK who: default::User { - SET default := (default::currentUser); - SET readonly := true; - }; - CREATE REQUIRED PROPERTY at: std::datetime { - SET default := (std::datetime_of_statement()); - SET readonly := true; - }; - CREATE REQUIRED PROPERTY status: ProgressReport::Status { - SET readonly := true; - }; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE PROPERTY transitionId: default::nanoid { - SET readonly := true; - }; - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (((((default::Role.Administrator IN givenRoles) OR ((default::Role.FieldPartner IN givenRoles) AND ((.transitionId IN {'5da76b5163', 'cb18f58cbf', '651d2a4dcc', 'e14c52346b'}) ?? false))) OR ((default::Role.Marketing IN givenRoles) AND ((.transitionId = '2d88e3cd6e') ?? false))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND ((.transitionId IN {'5da76b5163', 'cb18f58cbf', '651d2a4dcc', '580377ea2b', '0d854e832e', 'e14c52346b', '2b137bcd66', 'a0c0c48a8c', 'e3e11c86b9'}) ?? false))) OR ((default::Role.Translator IN givenRoles) AND ((.transitionId IN {'580377ea2b', '0d854e832e'}) ?? false))) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW UPDATE WRITE ; - CREATE PROPERTY notes: default::RichText { - SET readonly := true; - }; - }; - CREATE TYPE Project::Context { - CREATE ANNOTATION std::description := 'A type that holds a reference to a list of projects. This allows multiple objects to hold a reference to the same list. For example, Language & Ethnologue::Language share the same context / project list.'; - }; - CREATE ABSTRACT TYPE Mixin::Taggable { - CREATE MULTI PROPERTY tags: std::str; - }; - ALTER TYPE Project::ContextAware { - CREATE REQUIRED LINK projectContext: Project::Context { - ON TARGET DELETE DELETE SOURCE; - }; - }; - CREATE SCALAR TYPE default::ReportPeriod EXTENDING enum; - CREATE ABSTRACT TYPE Comments::Aware EXTENDING default::Resource { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForCommentable - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCommentable - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForCommentable - ALLOW UPDATE WRITE ; - }; - CREATE ABSTRACT TYPE Mixin::Postable EXTENDING default::Resource { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForPostable - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPostable - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Leadership'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPostable - ALLOW UPDATE WRITE ; - }; - CREATE ABSTRACT TYPE default::Project EXTENDING Mixin::Postable, Comments::Aware, default::Resource, Project::ContextAware, Mixin::Named, Mixin::Pinnable, Mixin::Taggable { - ALTER PROPERTY ownSensitivity { - SET default := (default::Sensitivity.High); - SET OWNED; - SET REQUIRED; - SET TYPE default::Sensitivity; - CREATE ANNOTATION std::description := 'The sensitivity of the project. This is user settable for internships and calculated for translation projects'; - }; - CREATE PROPERTY mouEnd: cal::local_date; - CREATE PROPERTY mouStart: cal::local_date; - CREATE REQUIRED PROPERTY step: Project::Step { - SET default := (Project::Step.EarlyConversations); - }; - CREATE PROPERTY status := (Project::statusFromStep(.step)); - ALTER LINK projectContext { - SET default := (INSERT - Project::Context - ); - ON SOURCE DELETE DELETE TARGET; - SET OWNED; - SET TYPE Project::Context; - }; - CREATE LINK rootDirectory: default::Directory; - CREATE PROPERTY departmentId: std::str { - CREATE CONSTRAINT std::exclusive; - CREATE CONSTRAINT std::expression ON (((__subject__ > 0) AND (std::len(__subject__) = 5))); - }; - CREATE PROPERTY estimatedSubmission: cal::local_date; - CREATE PROPERTY financialReportPeriod: default::ReportPeriod; - CREATE PROPERTY financialReportReceivedAt: std::datetime; - CREATE PROPERTY initialMouEnd: cal::local_date { - SET default := (.mouEnd); - CREATE REWRITE - UPDATE - USING ((.mouEnd IF (.status = Project::Status.InDevelopment) ELSE .initialMouEnd)); - }; - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE REQUIRED PROPERTY stepChangedAt: std::datetime { - SET default := (.createdAt); - CREATE REWRITE - UPDATE - USING ((std::datetime_of_statement() IF (.step != __old__.step) ELSE .stepChangedAt)); - }; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProject - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProject - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProject - ALLOW UPDATE WRITE ; - CREATE CONSTRAINT std::expression ON ((.mouEnd >= .mouStart)); - }; - ALTER TYPE Project::Context { - CREATE MULTI LINK projects: default::Project { - ON TARGET DELETE ALLOW; - }; - }; - ALTER TYPE Project::Child { - CREATE REQUIRED LINK project: default::Project { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - }; - ALTER TYPE Project::ContextAware { - CREATE REQUIRED SINGLE PROPERTY sensitivity := ((std::max(.projectContext.projects.ownSensitivity) ?? (.ownSensitivity ?? default::Sensitivity.High))); - }; - CREATE TYPE Project::Member EXTENDING Project::Child { - CREATE REQUIRED LINK user: default::User { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProjectMember - ALLOW UPDATE WRITE ; - CREATE CONSTRAINT std::exclusive ON ((.project, .user)); - CREATE MULTI PROPERTY roles: default::Role; - }; - ALTER TYPE default::Project { - CREATE MULTI LINK members := (.; - CREATE ABSTRACT TYPE default::Engagement EXTENDING Project::Child { - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForEngagement - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) AND .isMember) AND (.project.status = 'InDevelopment'))) - ); - CREATE PROPERTY completedDate: cal::local_date { - CREATE ANNOTATION std::description := 'Translation / Growth Plan complete date'; - }; - CREATE PROPERTY description: default::RichText; - CREATE PROPERTY disbursementCompleteDate: cal::local_date; - CREATE PROPERTY endDateOverride: cal::local_date; - CREATE PROPERTY endDate := ((.endDateOverride ?? .project.mouEnd)); - CREATE PROPERTY initialEndDate: cal::local_date; - CREATE PROPERTY lastReactivatedAt: std::datetime; - CREATE PROPERTY lastSuspendedAt: std::datetime; - CREATE PROPERTY startDateOverride: cal::local_date; - CREATE PROPERTY startDate := ((.startDateOverride ?? .project.mouStart)); - CREATE REQUIRED PROPERTY status: Engagement::Status { - SET default := (Engagement::Status.InDevelopment); - }; - CREATE PROPERTY statusModifiedAt: std::datetime { - CREATE REWRITE - UPDATE - USING ((std::datetime_of_statement() IF (.status != __old__.status) ELSE .statusModifiedAt)); - }; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForEngagement - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) AND .isMember) AND (.status = 'InDevelopment'))) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEngagement - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEngagement - ALLOW UPDATE WRITE ; - ALTER PROPERTY initialEndDate { - CREATE REWRITE - INSERT - USING ((.endDate IF (.status = Engagement::Status.InDevelopment) ELSE .initialEndDate)); - CREATE REWRITE - UPDATE - USING ((.endDate IF (.status = Engagement::Status.InDevelopment) ELSE .initialEndDate)); - }; - ALTER PROPERTY lastReactivatedAt { - CREATE REWRITE - UPDATE - USING ((std::datetime_of_statement() IF (((.status != __old__.status) AND (.status = Engagement::Status.Active)) AND (__old__.status = Engagement::Status.Suspended)) ELSE .lastReactivatedAt)); - }; - ALTER PROPERTY lastSuspendedAt { - CREATE REWRITE - UPDATE - USING ((std::datetime_of_statement() IF ((.status != __old__.status) AND (.status = Engagement::Status.Suspended)) ELSE .lastSuspendedAt)); - }; - }; - ALTER TYPE Comments::Thread { - ALTER LINK container { - SET SINGLE; - ON TARGET DELETE DELETE SOURCE; - SET OWNED; - SET REQUIRED; - SET TYPE Comments::Aware USING ({}); - }; - }; - ALTER TYPE Comments::Aware { - CREATE LINK commentThreads := (.{}); - }; - }; - ALTER TYPE Mixin::Postable { - CREATE LINK posts := (.{}); - }; - }; - CREATE TYPE default::LanguageEngagement EXTENDING default::Engagement { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForLanguageEngagement - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.ConsultantManager IN givenRoles) - ); - CREATE LINK pnp: default::File; - CREATE PROPERTY historicGoal: std::str { - CREATE ANNOTATION std::deprecated := 'Legacy data'; - }; - CREATE REQUIRED PROPERTY lukePartnership: std::bool { - SET default := false; - }; - CREATE REQUIRED PROPERTY openToInvestorVisit: std::bool { - SET default := false; - }; - CREATE PROPERTY paratextRegistryId: std::str; - CREATE PROPERTY sentPrintingDate: cal::local_date { - CREATE ANNOTATION std::deprecated := 'Legacy data'; - }; - }; - ALTER TYPE default::Project { - CREATE PROPERTY engagementTotal := (std::count(.{}); - }; - }; - ALTER TYPE default::TranslationProject { - CREATE MULTI LINK engagements := (.= 0) AND (__subject__ <= 9))); - }; - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFundingAccount - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFundingAccount - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'ConsultantManager', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFundingAccount - ALLOW UPDATE WRITE ; - }; - CREATE SCALAR TYPE Location::IsoAlpha3Code EXTENDING std::str { - CREATE CONSTRAINT std::regexp('^[A-Z]{3}$'); - }; - CREATE SCALAR TYPE Location::Type EXTENDING enum; - CREATE TYPE default::Location EXTENDING default::Resource, Mixin::Named { - CREATE LINK fundingAccount: default::FundingAccount; - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForLocation - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE LINK mapImage: default::File; - CREATE ACCESS POLICY CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForLocation - ALLOW SELECT, UPDATE ; - CREATE LINK defaultMarketingRegion: default::Location; - CREATE PROPERTY isoAlpha3: Location::IsoAlpha3Code { - CREATE CONSTRAINT std::exclusive; - }; - CREATE REQUIRED PROPERTY type: Location::Type; - }; - ALTER TYPE default::Project { - CREATE LINK primaryLocation: default::Location; - ALTER PROPERTY departmentId { - CREATE REWRITE - INSERT - USING ((IF ((NOT (EXISTS (.departmentId)) AND (.status <= Project::Status.Active)) AND (.step >= Project::Step.PendingFinanceConfirmation)) THEN (WITH - fa := - std::assert_exists(__subject__.primaryLocation.fundingAccount, message := 'Project must have a primary location') - , - existing := - (SELECT - (DETACHED default::Project).departmentId - FILTER - (default::Project.primaryLocation.fundingAccount = fa) - ) - , - available := - (std::range_unpack(std::range(((fa.accountNumber * 10000) + 11), ((fa.accountNumber * 10000) + 9999))) EXCEPT existing) - SELECT - std::min(available) - ) ELSE .departmentId)); - CREATE REWRITE - UPDATE - USING ((IF ((NOT (EXISTS (.departmentId)) AND (.status <= Project::Status.Active)) AND (.step >= Project::Step.PendingFinanceConfirmation)) THEN (WITH - fa := - std::assert_exists(__subject__.primaryLocation.fundingAccount, message := 'Project must have a primary location') - , - existing := - (SELECT - (DETACHED default::Project).departmentId - FILTER - (default::Project.primaryLocation.fundingAccount = fa) - ) - , - available := - (std::range_unpack(std::range(((fa.accountNumber * 10000) + 11), ((fa.accountNumber * 10000) + 9999))) EXCEPT existing) - SELECT - std::min(available) - ) ELSE .departmentId)); - }; - }; - CREATE ABSTRACT TYPE default::PeriodicReport EXTENDING default::Resource, Mixin::Embedded { - CREATE REQUIRED PROPERTY period: range; - CREATE PROPERTY `end` := (default::date_range_get_upper(.period)); - CREATE LINK reportFile: default::File; - CREATE PROPERTY receivedDate: cal::local_date; - CREATE PROPERTY skippedReason: std::str; - CREATE PROPERTY `start` := (std::range_get_lower(.period)); - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForPeriodicReport - ALLOW UPDATE WRITE, DELETE, INSERT ; - }; - CREATE TYPE default::FinancialReport EXTENDING default::PeriodicReport, Project::Child { - ALTER LINK container { - SET OWNED; - SET TYPE default::Project USING ({}); - }; - }; - CREATE TYPE default::NarrativeReport EXTENDING default::PeriodicReport, Project::Child { - ALTER LINK container { - SET OWNED; - SET TYPE default::Project USING ({}); - }; - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForNarrativeReport - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - , - isMember := - (.container[IS Project::ContextAware].isMember ?? false) - SELECT - (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT givenRoles)) AND isMember) - ); - }; - ALTER TYPE Engagement::Child { - CREATE REQUIRED LINK engagement: default::Engagement { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - }; - CREATE TYPE default::ProgressReport EXTENDING default::PeriodicReport, Engagement::Child { - ALTER LINK container { - SET OWNED; - SET TYPE default::LanguageEngagement USING ({}); - }; - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReport - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember) - ); - ALTER LINK engagement { - SET OWNED; - SET TYPE default::LanguageEngagement USING ({}); - }; - }; - CREATE FUNCTION default::hydrate(typeName: std::str, scopedValue: std::json) -> std::str USING (typeName); - CREATE FUNCTION default::str_clean(string: std::str) -> OPTIONAL std::str USING (WITH - trimmed := - std::str_trim(string, ' \t\r\n') - SELECT - (IF (std::len(trimmed) > 0) THEN trimmed ELSE {}) - ); - ALTER TYPE Mixin::Named { - ALTER PROPERTY name { - CREATE REWRITE - INSERT - USING (default::str_clean(.name)); - CREATE REWRITE - UPDATE - USING (default::str_clean(.name)); - }; - }; - CREATE FUNCTION default::str_sortable(value: std::str) -> std::str USING (std::str_lower(std::re_replace('Ñ', 'N', std::str_trim(std::re_replace(r'[ [\]|,\-$]+', ' ', value, flags := 'g')), flags := 'g'))); - ALTER TYPE Mixin::Named { - CREATE INDEX ON (default::str_sortable(.name)); - }; - CREATE ABSTRACT TYPE default::Producible EXTENDING default::Resource, Mixin::Named { - ALTER PROPERTY name { - SET OWNED; - CREATE DELEGATED CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProducible - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProducible - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'FieldOperationsDirector', 'ProjectManager', 'RegionalDirector'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForProducible - ALLOW SELECT, UPDATE ; - }; - CREATE TYPE default::EthnoArt EXTENDING default::Producible; - CREATE TYPE default::FieldRegion EXTENDING default::Resource, Mixin::Named { - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFieldRegion - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFieldRegion - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFieldRegion - ALLOW UPDATE WRITE ; - CREATE REQUIRED LINK director: default::User; - }; - CREATE TYPE default::FieldZone EXTENDING default::Resource, Mixin::Named { - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFieldZone - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFieldZone - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFieldZone - ALLOW UPDATE WRITE ; - CREATE REQUIRED LINK director: default::User; - }; - CREATE TYPE default::Film EXTENDING default::Producible; - ALTER TYPE default::Project { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProject - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Fundraising', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'RegionalDirector', 'FieldOperationsDirector', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - }; - ALTER TYPE default::InternshipProject { - CREATE MULTI LINK engagements := (.= 0)); - }; - CREATE TYPE default::Language EXTENDING Mixin::Postable, default::Resource, Project::ContextAware, Mixin::Named, Mixin::Pinnable, Mixin::Taggable { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForLanguage - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - ALTER LINK projectContext { - SET default := (INSERT - Project::Context - ); - ON SOURCE DELETE DELETE TARGET; - SET OWNED; - SET TYPE Project::Context; - }; - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForLanguage - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Fundraising', 'Marketing', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - ALTER PROPERTY ownSensitivity { - SET default := (default::Sensitivity.High); - SET OWNED; - SET REQUIRED; - SET TYPE default::Sensitivity; - CREATE ANNOTATION std::description := 'The sensitivity of the language. This is a source / user settable.'; - }; - CREATE PROPERTY populationOverride: default::population; - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForLanguage - ALLOW UPDATE WRITE ; - CREATE OPTIONAL LINK firstScriptureEngagement: default::LanguageEngagement; - CREATE REQUIRED PROPERTY hasExternalFirstScripture: std::bool { - SET default := false; - }; - CREATE CONSTRAINT std::expression ON (((EXISTS (.firstScriptureEngagement) AND NOT (.hasExternalFirstScripture)) OR NOT (EXISTS (.firstScriptureEngagement)))); - CREATE REQUIRED PROPERTY isDialect: std::bool { - SET default := false; - }; - CREATE REQUIRED PROPERTY isSignLanguage: std::bool { - SET default := false; - }; - CREATE REQUIRED PROPERTY leastOfThese: std::bool { - SET default := false; - }; - CREATE INDEX ON ((.name, .ownSensitivity, .leastOfThese, .isSignLanguage, .isDialect)); - CREATE REQUIRED PROPERTY displayName: std::str { - SET default := (.name); - }; - CREATE PROPERTY displayNamePronunciation: std::str; - CREATE PROPERTY leastOfTheseReason: std::str; - CREATE PROPERTY registryOfDialectsCode: std::str { - CREATE CONSTRAINT std::exclusive; - CREATE CONSTRAINT std::regexp('^[0-9]{5}$'); - }; - CREATE PROPERTY signLanguageCode: std::str { - CREATE CONSTRAINT std::regexp(r'^[A-Z]{2}\d{2}$'); - }; - CREATE PROPERTY sponsorEstimatedEndDate: cal::local_date; - }; - ALTER TYPE default::Location { - CREATE LINK defaultFieldRegion: default::FieldRegion; - }; - CREATE SCALAR TYPE Organization::Reach EXTENDING enum; - CREATE SCALAR TYPE Organization::Type EXTENDING enum; - CREATE TYPE default::Organization EXTENDING default::Resource, Project::ContextAware, Mixin::Named { - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForOrganization - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Controller'} INTERSECT givenRoles)) - ); - ALTER LINK projectContext { - SET default := (INSERT - Project::Context - ); - ON SOURCE DELETE DELETE TARGET; - SET OWNED; - SET TYPE Project::Context; - }; - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForOrganization - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForOrganization - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (((EXISTS (({'Administrator', 'ConsultantManager', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT givenRoles)) AND .isMember)) OR (EXISTS (({'ExperienceOperations', 'Fundraising'} INTERSECT givenRoles)) AND (.sensitivity <= default::Sensitivity.Medium))) OR ((default::Role.Marketing IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Low)))) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForOrganization - ALLOW UPDATE WRITE ; - CREATE PROPERTY acronym: std::str; - CREATE MULTI PROPERTY reach: Organization::Reach; - CREATE MULTI PROPERTY types: Organization::Type; - }; - CREATE SCALAR TYPE Partner::Type EXTENDING enum; - CREATE SCALAR TYPE Partnership::FinancialReportingType EXTENDING enum; - CREATE TYPE default::Partner EXTENDING Mixin::Postable, default::Resource, Project::ContextAware, Mixin::Named, Mixin::Pinnable, Mixin::Taggable { - ALTER PROPERTY name { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForPartner - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'Controller'} INTERSECT givenRoles)) - ); - CREATE REQUIRED LINK organization: default::Organization { - ON SOURCE DELETE DELETE TARGET; - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForPartner - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPartner - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((((EXISTS (({'Administrator', 'ConsultantManager', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT givenRoles)) AND .isMember)) OR (EXISTS (({'ExperienceOperations', 'Fundraising'} INTERSECT givenRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR ((default::Role.Marketing IN givenRoles) AND ((.isMember AND (.sensitivity <= default::Sensitivity.Medium)) OR (.sensitivity <= default::Sensitivity.Low)))) OR ((default::Role.StaffMember IN givenRoles) AND (.sensitivity <= default::Sensitivity.Low))) - ); - CREATE MULTI LINK fieldRegions: default::FieldRegion; - CREATE LINK languageOfWiderCommunication: default::Language; - CREATE MULTI LINK languagesOfConsulting: default::Language; - CREATE MULTI LINK countries: default::Location; - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPartner - ALLOW UPDATE WRITE ; - CREATE LINK pointOfContact: default::User; - CREATE REQUIRED PROPERTY active: std::bool { - SET default := true; - }; - CREATE MULTI PROPERTY financialReportingTypes: Partnership::FinancialReportingType; - CREATE REQUIRED PROPERTY globalInnovationsClient: std::bool { - SET default := false; - }; - CREATE PROPERTY pmcEntityCode: std::str { - CREATE CONSTRAINT std::regexp('^[A-Z]{3}$'); - }; - CREATE MULTI PROPERTY types: Partner::Type; - }; - CREATE TYPE default::Story EXTENDING default::Producible; - CREATE TYPE Budget::Record EXTENDING Project::Child { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForBudgetRecord - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) - ); - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudgetRecord - ALLOW UPDATE WRITE, DELETE, INSERT ; - CREATE REQUIRED LINK organization: default::Organization { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - CREATE REQUIRED PROPERTY fiscalYear: std::int16 { - SET readonly := true; - }; - CREATE PROPERTY amount: std::float32; - }; - CREATE ABSTRACT TYPE Engagement::Ceremony EXTENDING Engagement::Child { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCeremony - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'FieldOperationsDirector', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE PROPERTY actualDate: cal::local_date; - CREATE PROPERTY estimatedDate: cal::local_date; - CREATE REQUIRED PROPERTY planned: std::bool { - SET default := false; - }; - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForCeremony - ALLOW UPDATE WRITE, DELETE, INSERT ; - CREATE CONSTRAINT std::exclusive ON (.engagement); - }; - CREATE TYPE Engagement::CertificationCeremony EXTENDING Engagement::Ceremony; - CREATE TYPE Engagement::DedicationCeremony EXTENDING Engagement::Ceremony; - CREATE SCALAR TYPE Ethnologue::code EXTENDING std::str { - CREATE CONSTRAINT std::regexp('^[a-z]{3}$'); - }; - CREATE TYPE Ethnologue::Language EXTENDING Project::ContextAware { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForEthnologueLanguage - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEthnologueLanguage - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((((EXISTS (({'Administrator', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.sensitivity <= default::Sensitivity.Medium))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT givenRoles)) AND .isMember)) OR ((default::Role.Fundraising IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Marketing', 'Fundraising', 'ExperienceOperations'} INTERSECT givenRoles)) AND (.sensitivity <= default::Sensitivity.Low))) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEthnologueLanguage - ALLOW UPDATE WRITE ; - CREATE REQUIRED LINK language: default::Language { - ON TARGET DELETE DELETE SOURCE; - CREATE CONSTRAINT std::exclusive; - }; - CREATE PROPERTY population: default::population; - CREATE PROPERTY code: Ethnologue::code { - CREATE CONSTRAINT std::exclusive; - }; - CREATE PROPERTY name: std::str; - CREATE PROPERTY provisionalCode: Ethnologue::code { - CREATE CONSTRAINT std::exclusive; - }; - }; - CREATE ABSTRACT TYPE Media::Temporal EXTENDING default::Media { - CREATE REQUIRED PROPERTY duration: std::int32; - }; - CREATE TYPE Media::Audio EXTENDING Media::Temporal; - CREATE ABSTRACT TYPE Media::Visual EXTENDING default::Media { - CREATE REQUIRED PROPERTY dimensions: tuple; - }; - CREATE TYPE Media::Image EXTENDING Media::Visual; - CREATE TYPE Media::Video EXTENDING Media::Visual, Media::Temporal; - ALTER TYPE ProgressReport::Child { - CREATE REQUIRED LINK report: default::ProgressReport { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - }; - CREATE TYPE ProgressReport::VarianceExplanation EXTENDING ProgressReport::Child { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForProgressReportVarianceExplanation - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportVarianceExplanation - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT givenRoles)) AND .isMember)) - ); - ALTER LINK report { - SET OWNED; - CREATE CONSTRAINT std::exclusive; - }; - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportVarianceExplanation - ALLOW UPDATE WRITE ; - CREATE PROPERTY comments: default::RichText; - CREATE MULTI PROPERTY reasons: std::str; - }; - ALTER TYPE ProgressReport::CommunityStory { - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - }; - ALTER TYPE ProgressReport::Highlight { - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - }; - ALTER TYPE ProgressReport::Media { - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportMedia - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (((((default::Role.Administrator IN givenRoles) OR (((default::Role.FieldPartner IN givenRoles) AND .isMember) AND (.variant = 'draft'))) OR ((default::Role.Marketing IN givenRoles) AND (.variant = 'published'))) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember) AND (.variant IN {'draft', 'translated', 'fpm'}))) OR (((default::Role.Translator IN givenRoles) AND .isMember) AND (.variant = 'translated'))) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportMedia - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((((((EXISTS (({'Administrator', 'Leadership', 'Marketing'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT givenRoles)) AND .isMember)) OR (((default::Role.FieldPartner IN givenRoles) AND .isMember) AND (.variant = 'draft'))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND (((.isMember AND (.variant IN {'draft', 'translated', 'fpm'})) OR ((.sensitivity <= default::Sensitivity.Low) AND (.variant IN {'fpm', 'published'}))) OR .isMember))) OR ((default::Role.Translator IN givenRoles) AND ((.isMember AND (.variant = 'translated')) OR .isMember))) OR (.isOwner ?? false)) - ); - }; - ALTER TYPE ProgressReport::TeamNews { - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((default::Role.Administrator IN givenRoles) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - }; - CREATE SCALAR TYPE Product::Step EXTENDING enum; - CREATE SCALAR TYPE ProgressReport::ProductProgress::Variant EXTENDING enum; - CREATE TYPE ProgressReport::ProductProgress::Step EXTENDING Mixin::Timestamped, Project::ContextAware { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForStepProgress - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE REQUIRED PROPERTY variant: ProgressReport::ProductProgress::Variant; - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForStepProgress - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((((EXISTS (({'Administrator', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'StaffMember'} INTERSECT givenRoles)) OR (((default::Role.FieldPartner IN givenRoles) AND .isMember) AND (.variant = 'partner'))) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember) AND (.variant IN {'official', 'partner'}))) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForStepProgress - ALLOW UPDATE WRITE ; - CREATE REQUIRED LINK report: default::ProgressReport; - CREATE REQUIRED PROPERTY step: Product::Step; - CREATE PROPERTY completed: std::float32; - }; - ALTER TYPE Project::Member { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForProjectMember - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND .isMember)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProjectMember - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'Translator'} INTERSECT givenRoles)) AND .isMember)) - ); - }; - ALTER TYPE User::Education { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForEducation - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForEducation - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEducation - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'ConsultantManager', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT givenRoles)) - ); - }; - ALTER TYPE User::Unavailability { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForUnavailability - ALLOW DELETE USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (default::Role.Administrator IN givenRoles) - ); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForUnavailability - ALLOW INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForUnavailability - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT givenRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'}))) - ); - }; - CREATE SCALAR TYPE Partnership::AgreementStatus EXTENDING enum; - CREATE TYPE default::Partnership EXTENDING Project::Child { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForPartnership - ALLOW DELETE, INSERT USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT givenRoles)) AND .isMember)) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) - ); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPartnership - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'Leadership', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT givenRoles)) AND .isMember)) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT givenRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR ((default::Role.StaffMember IN givenRoles) AND (.sensitivity <= default::Sensitivity.Low))) - ); - CREATE REQUIRED LINK partner: default::Partner; - CREATE LINK organization := (.partner.organization); - CREATE LINK agreement: default::File; - CREATE LINK mou: default::File; - CREATE CONSTRAINT std::exclusive ON ((.project, .partner)); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPartnership - ALLOW UPDATE WRITE ; - CREATE PROPERTY mouEndOverride: cal::local_date; - CREATE PROPERTY mouEnd := ((.mouEndOverride ?? .project.mouEnd)); - CREATE PROPERTY mouStartOverride: cal::local_date; - CREATE PROPERTY mouStart := ((.mouStartOverride ?? .project.mouStart)); - CREATE REQUIRED PROPERTY agreementStatus: Partnership::AgreementStatus { - SET default := (Partnership::AgreementStatus.NotAttached); - }; - CREATE PROPERTY financialReportingType: Partnership::FinancialReportingType; - CREATE REQUIRED PROPERTY mouStatus: Partnership::AgreementStatus { - SET default := (Partnership::AgreementStatus.NotAttached); - }; - CREATE REQUIRED PROPERTY primary: std::bool { - SET default := false; - }; - CREATE MULTI PROPERTY types: Partner::Type; - }; - CREATE SCALAR TYPE Budget::Status EXTENDING enum; - CREATE TYPE default::Budget EXTENDING Project::Child { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForBudget - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - SELECT - (EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT givenRoles)) OR ((default::Role.ConsultantManager IN givenRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) - ); - CREATE REQUIRED PROPERTY status: Budget::Status { - SET default := (Budget::Status.Pending); - }; - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudget - ALLOW UPDATE WRITE, DELETE, INSERT ; - CREATE LINK universalTemplate: default::File; - }; - ALTER TYPE default::PeriodicReport { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPeriodicReport - ALLOW SELECT, UPDATE READ USING (WITH - givenRoles := - (GLOBAL default::currentUserId).roles - , - isMember := - (.container[IS Project::ContextAware].isMember ?? false) - , - sensitivity := - (.container[IS Project::ContextAware].sensitivity ?? default::Sensitivity.High) - SELECT - ((EXISTS (({'Administrator', 'ExperienceOperations', 'FieldOperationsDirector', 'FieldPartner', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT givenRoles)) OR (EXISTS (({'ConsultantManager', 'Marketing', 'Fundraising', 'ExperienceOperations'} INTERSECT givenRoles)) AND (isMember OR (sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor', 'Translator'} INTERSECT givenRoles)) AND isMember)) - ); - }; - ALTER TYPE default::LanguageEngagement { - CREATE REQUIRED LINK language: default::Language { - SET readonly := true; - }; - }; - ALTER TYPE default::Language { - CREATE LINK engagements := (SELECT - default::LanguageEngagement - FILTER - (__source__ = .language) - ); - }; - ALTER TYPE default::FieldRegion { - CREATE REQUIRED LINK fieldZone: default::FieldZone; - }; - ALTER TYPE default::FieldZone { - CREATE LINK fieldRegions := (. 0)) OR NOT (EXISTS (__new__.primaryLocation))), message := 'Project must have a primary location with a specified funding account')); - CREATE LINK fieldRegion: default::FieldRegion; - CREATE LINK marketingRegionOverride: default::FieldRegion; - CREATE LINK marketingLocation: default::Location; - CREATE MULTI LINK otherLocations: default::Location; - }; - ALTER TYPE default::TranslationProject { - CREATE TRIGGER confirmProjectSens - AFTER UPDATE - FOR EACH DO (std::assert((__new__.ownSensitivity = (std::max(__new__.languages.ownSensitivity) ?? default::Sensitivity.High)), message := 'TranslationProject sensitivity is automatically set to (and required to be) the highest sensitivity Language engaged')); - }; - ALTER TYPE default::Language { - CREATE LINK projects := (SELECT - default::TranslationProject - FILTER - (__source__ = .languages) - ); - CREATE TRIGGER recalculateProjectSens - AFTER UPDATE - FOR EACH DO (UPDATE - (SELECT - __new__.projects - FILTER - (.ownSensitivity != (std::max(.languages.ownSensitivity) ?? default::Sensitivity.High)) - ) - SET { - ownSensitivity := (std::max(.languages.ownSensitivity) ?? default::Sensitivity.High) - }); - CREATE TRIGGER connectEthnologue - AFTER INSERT - FOR EACH DO (((SELECT - Ethnologue::Language - FILTER - (.language = __new__) - ) ?? (INSERT - Ethnologue::Language - { - language := __new__, - ownSensitivity := __new__.ownSensitivity, - projectContext := __new__.projectContext - }))); - CREATE REQUIRED SINGLE LINK ethnologue := (std::assert_exists(std::assert_single(.; - CREATE TYPE Product::PartnershipProducingMedium EXTENDING Engagement::Child { - CREATE REQUIRED LINK partnership: default::Partnership { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - CREATE REQUIRED PROPERTY medium: Product::Medium; - CREATE CONSTRAINT std::exclusive ON ((.engagement, .partnership, .medium)); - }; - CREATE SCALAR TYPE Product::Methodology EXTENDING enum; - CREATE SCALAR TYPE Product::ProgressMeasurement EXTENDING enum; - CREATE SCALAR TYPE Product::Purpose EXTENDING enum; - CREATE ABSTRACT TYPE default::Product EXTENDING Engagement::Child { - CREATE PROPERTY describeCompletion: std::str; - CREATE MULTI PROPERTY mediums: Product::Medium; - CREATE PROPERTY methodology: Product::Methodology; - CREATE PROPERTY placeholderDescription: std::str; - CREATE PROPERTY pnpIndex: std::int16; - CREATE PROPERTY progressStepMeasurement: Product::ProgressMeasurement; - CREATE PROPERTY progressTarget: std::int16; - CREATE MULTI PROPERTY purposes: Product::Purpose; - CREATE MULTI PROPERTY steps: Product::Step; - }; - CREATE TYPE default::DerivativeScriptureProduct EXTENDING default::Product { - CREATE REQUIRED LINK produces: default::Producible; - CREATE REQUIRED PROPERTY composite: std::bool { - SET default := false; - }; - CREATE PROPERTY totalVerseEquivalents: std::float32; - CREATE PROPERTY totalVerses: std::int16; - }; - CREATE TYPE Scripture::UnspecifiedPortion { - CREATE REQUIRED PROPERTY book: std::str { - SET readonly := true; - }; - CREATE REQUIRED PROPERTY totalVerses: std::int16 { - SET readonly := true; - CREATE CONSTRAINT std::min_value(1); - }; - }; - CREATE TYPE default::DirectScriptureProduct EXTENDING default::Product { - CREATE LINK unspecifiedScripture: Scripture::UnspecifiedPortion { - ON SOURCE DELETE DELETE TARGET; - }; - CREATE TRIGGER deleteOldUnspecifiedScripture - AFTER UPDATE - FOR EACH - WHEN ((__old__.unspecifiedScripture ?!= __new__.unspecifiedScripture)) - DO (DELETE - __old__.unspecifiedScripture - ); - CREATE PROPERTY totalVerseEquivalents: std::float32; - CREATE PROPERTY totalVerses: std::int16; - }; - CREATE TYPE default::OtherProduct EXTENDING default::Product { - CREATE PROPERTY description: std::str; - CREATE REQUIRED PROPERTY title: std::str; - }; - ALTER TYPE default::Post { - CREATE SINGLE PROPERTY isMember := (.container[IS Project::ContextAware].isMember); - CREATE SINGLE PROPERTY sensitivity := (.container[IS Project::ContextAware].sensitivity); - }; - ALTER TYPE Project::Child { - CREATE TRIGGER enforceCorrectProjectContext - AFTER UPDATE, INSERT - FOR EACH DO (std::assert((__new__.projectContext = __new__.project.projectContext), message := "Given project context must match given project's context")); - }; - ALTER TYPE Budget::Record { - CREATE REQUIRED LINK budget: default::Budget { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - CREATE CONSTRAINT std::exclusive ON ((.budget, .fiscalYear, .organization)); - }; - ALTER TYPE Engagement::Child { - CREATE TRIGGER enforceEngagementProject - AFTER UPDATE, INSERT - FOR EACH DO (std::assert((__new__.engagement.project = __new__.project), message := 'Given engagement must be for the same project as the given project.')); - }; - ALTER TYPE ProgressReport::Child { - CREATE TRIGGER enforceProgressReportEngagement - AFTER UPDATE, INSERT - FOR EACH DO (std::assert((__new__.report.engagement = __new__.engagement), message := 'Given progress report must be for the same engagement as the given engagement')); - }; - ALTER TYPE ProgressReport::CommunityStory { - ALTER LINK container { - SET OWNED; - SET TYPE default::ProgressReport USING ({}); - }; - }; - ALTER TYPE ProgressReport::Highlight { - ALTER LINK container { - SET OWNED; - SET TYPE default::ProgressReport USING ({}); - }; - }; - ALTER TYPE ProgressReport::TeamNews { - ALTER LINK container { - SET OWNED; - SET TYPE default::ProgressReport USING ({}); - }; - }; - ALTER TYPE default::Budget { - CREATE LINK records := (.; - CREATE TYPE ProgressReport::ProductProgress::Summary { - CREATE REQUIRED LINK report: default::ProgressReport; - CREATE REQUIRED PROPERTY period: ProgressReport::ProductProgress::Period; - CREATE CONSTRAINT std::exclusive ON ((.report, .period)); - CREATE REQUIRED PROPERTY actual: std::float32; - CREATE REQUIRED PROPERTY planned: std::float32; - }; - CREATE TYPE Scripture::Collection { - CREATE REQUIRED PROPERTY label: std::str { - SET readonly := true; - }; - }; - ALTER TYPE default::Product { - CREATE LINK scripture: Scripture::Collection { - ON SOURCE DELETE DELETE TARGET; - }; - }; - ALTER TYPE default::DirectScriptureProduct { - CREATE TRIGGER deleteOldScripture - AFTER UPDATE - FOR EACH - WHEN ((__old__.scripture ?!= __new__.scripture)) - DO (DELETE - __old__.scripture - ); - }; - ALTER TYPE default::DerivativeScriptureProduct { - CREATE LINK scriptureOverride: Scripture::Collection { - ON SOURCE DELETE DELETE TARGET; - }; - CREATE TRIGGER deleteOldScriptureOverride - AFTER UPDATE - FOR EACH - WHEN ((__old__.scriptureOverride ?!= __new__.scriptureOverride)) - DO (DELETE - __old__.scriptureOverride - ); - ALTER LINK scripture { - ON SOURCE DELETE ALLOW; - SET OWNED; - }; - }; - ALTER TYPE default::Producible { - CREATE LINK scripture: Scripture::Collection { - ON SOURCE DELETE DELETE TARGET; - }; - CREATE TRIGGER updateDerivativeProducts - AFTER UPDATE - FOR EACH - WHEN ((__old__.scripture ?!= __new__.scripture)) - DO (UPDATE - default::DerivativeScriptureProduct - FILTER - ((.produces = __new__) AND NOT (EXISTS (.scriptureOverride))) - SET { - scripture := __new__.scripture - }); - }; - CREATE TYPE Scripture::VerseRange { - CREATE REQUIRED PROPERTY label: std::str { - SET readonly := true; - }; - }; - ALTER TYPE Scripture::Collection { - CREATE MULTI LINK verses: Scripture::VerseRange { - ON SOURCE DELETE DELETE TARGET; - ON TARGET DELETE DEFERRED RESTRICT; - SET readonly := true; - }; - }; - CREATE TYPE Scripture::Verse { - CREATE REQUIRED PROPERTY verseId: std::int16 { - SET readonly := true; - CREATE CONSTRAINT std::expression ON (((__subject__ >= 0) AND (__subject__ <= 31101))); - }; - CREATE REQUIRED PROPERTY book: std::str { - SET readonly := true; - }; - CREATE REQUIRED PROPERTY chapter: std::int16 { - SET readonly := true; - CREATE CONSTRAINT std::expression ON (((__subject__ >= 1) AND (__subject__ <= 150))); - }; - CREATE REQUIRED PROPERTY verse: std::int16 { - SET readonly := true; - CREATE CONSTRAINT std::expression ON (((__subject__ >= 1) AND (__subject__ <= 176))); - }; - CREATE PROPERTY label := (((((.book ++ ' ') ++ .chapter) ++ ':') ++ .verse)); - }; - ALTER TYPE Scripture::VerseRange { - CREATE REQUIRED LINK `end`: Scripture::Verse { - ON SOURCE DELETE DELETE TARGET; - SET readonly := true; - }; - CREATE REQUIRED LINK `start`: Scripture::Verse { - ON SOURCE DELETE DELETE TARGET; - SET readonly := true; - }; - CREATE PROPERTY ids := (std::range(.`start`.verseId, .`end`.verseId, inc_upper := true)); - }; - ALTER TYPE Scripture::Collection { - CREATE PROPERTY ids := (std::multirange(std::array_agg(.verses.ids))); - }; - ALTER TYPE default::DerivativeScriptureProduct { - ALTER LINK scripture { - CREATE REWRITE - INSERT - USING ((IF EXISTS (.scriptureOverride) THEN (IF EXISTS (.scriptureOverride.verses) THEN .scriptureOverride ELSE {}) ELSE .produces.scripture)); - CREATE REWRITE - UPDATE - USING ((IF EXISTS (.scriptureOverride) THEN (IF EXISTS (.scriptureOverride.verses) THEN .scriptureOverride ELSE {}) ELSE .produces.scripture)); - }; - }; - ALTER TYPE default::Product { - CREATE TRIGGER denyEmptyScriptureCollection - AFTER UPDATE, INSERT - FOR EACH DO (std::assert((NOT (EXISTS (__new__.scripture)) OR EXISTS (__new__.scripture.verses)), message := '`Product.scripture` should have a `Scripture::Collection` with verses or be null/empty-set')); - }; - ALTER TYPE default::Producible { - CREATE TRIGGER denyEmptyScriptureCollection - AFTER UPDATE, INSERT - FOR EACH DO (std::assert((NOT (EXISTS (__new__.scripture)) OR EXISTS (__new__.scripture.verses)), message := '`Producible.scripture` should have a `Scripture::Collection` with verses or be null/empty-set')); - }; - CREATE TYPE default::Alias { - CREATE REQUIRED LINK target: std::Object { - ON TARGET DELETE DELETE SOURCE; - }; - CREATE REQUIRED PROPERTY name: std::str { - CREATE CONSTRAINT std::exclusive; - }; - }; - CREATE SCALAR TYPE default::Sens EXTENDING default::Sensitivity; -}; diff --git a/dbschema/migrations/00001-m1zafss.edgeql b/dbschema/migrations/00001-m1zafss.edgeql new file mode 100644 index 0000000000..7b34305e91 --- /dev/null +++ b/dbschema/migrations/00001-m1zafss.edgeql @@ -0,0 +1,708 @@ +CREATE MIGRATION m1zafsshfknkck6a7esom7ck425amgmeqx2pds6zimbvsajhz2ekfq + ONTO initial +{ + CREATE MODULE Auth IF NOT EXISTS; + CREATE MODULE Engagement IF NOT EXISTS; + CREATE MODULE Ethnologue IF NOT EXISTS; + CREATE MODULE File IF NOT EXISTS; + CREATE MODULE Location IF NOT EXISTS; + CREATE MODULE Mixin IF NOT EXISTS; + CREATE MODULE Product IF NOT EXISTS; + CREATE MODULE Project IF NOT EXISTS; + CREATE MODULE User IF NOT EXISTS; + CREATE SCALAR TYPE Project::Status EXTENDING enum; + CREATE SCALAR TYPE Project::Step EXTENDING enum; + CREATE FUNCTION Project::statusFromStep(step: Project::Step) -> Project::Status USING (WITH + dev := + {Project::Step.EarlyConversations, Project::Step.PendingConceptApproval, Project::Step.PrepForConsultantEndorsement, Project::Step.PendingConsultantEndorsement, Project::Step.PrepForFinancialEndorsement, Project::Step.PendingFinancialEndorsement, Project::Step.FinalizingProposal, Project::Step.PendingRegionalDirectorApproval, Project::Step.PendingZoneDirectorApproval, Project::Step.PendingFinanceConfirmation, Project::Step.OnHoldFinanceConfirmation} + , + active := + {Project::Step.Active, Project::Step.ActiveChangedPlan, Project::Step.DiscussingChangeToPlan, Project::Step.PendingChangeToPlanApproval, Project::Step.PendingChangeToPlanConfirmation, Project::Step.DiscussingSuspension, Project::Step.PendingSuspensionApproval, Project::Step.Suspended, Project::Step.DiscussingReactivation, Project::Step.PendingReactivationApproval, Project::Step.DiscussingTermination, Project::Step.PendingTerminationApproval, Project::Step.FinalizingCompletion} + SELECT + (Project::Status.InDevelopment IF (step IN dev) ELSE (Project::Status.Active IF (step IN active) ELSE (Project::Status.DidNotDevelop IF (step = Project::Step.DidNotDevelop) ELSE (Project::Status.DidNotDevelop IF (step = Project::Step.Rejected) ELSE (Project::Status.Terminated IF (step = Project::Step.Terminated) ELSE (Project::Status.Completed IF (step = Project::Step.Completed) ELSE Project::Status.InDevelopment)))))) + ); + CREATE GLOBAL default::currentActorId -> std::uuid; + CREATE SCALAR TYPE default::Role EXTENDING enum; + CREATE ABSTRACT TYPE default::Actor { + CREATE MULTI PROPERTY roles: default::Role; + }; + CREATE GLOBAL default::currentActor := (SELECT + default::Actor + FILTER + (.id = GLOBAL default::currentActorId) + ); + CREATE ABSTRACT TYPE Mixin::Named { + CREATE REQUIRED PROPERTY name: std::str; + CREATE INDEX fts::index ON (fts::with_options(.name, language := fts::Language.eng)); + }; + CREATE ABSTRACT TYPE Mixin::Taggable { + CREATE MULTI PROPERTY tags: std::str; + }; + CREATE ABSTRACT TYPE Mixin::Timestamped { + CREATE REQUIRED PROPERTY createdAt: std::datetime { + SET default := (std::datetime_of_statement()); + SET readonly := true; + }; + CREATE REQUIRED PROPERTY modifiedAt: std::datetime { + SET default := (std::datetime_of_statement()); + CREATE REWRITE + UPDATE + USING (std::datetime_of_statement()); + }; + }; + CREATE SCALAR TYPE Project::Type EXTENDING enum; + CREATE SCALAR TYPE default::ReportPeriod EXTENDING enum; + CREATE SCALAR TYPE default::Sensitivity EXTENDING enum; + CREATE ABSTRACT TYPE Mixin::Audited EXTENDING Mixin::Timestamped { + CREATE REQUIRED LINK createdBy: default::Actor { + SET default := (GLOBAL default::currentActor); + SET readonly := true; + }; + CREATE REQUIRED LINK modifiedBy: default::Actor { + SET default := (GLOBAL default::currentActor); + CREATE REWRITE + UPDATE + USING (GLOBAL default::currentActor); + }; + CREATE REQUIRED PROPERTY isCreator := ((.createdBy ?= GLOBAL default::currentActor)); + }; + CREATE ABSTRACT TYPE Mixin::Pinnable; + CREATE ABSTRACT TYPE Project::ContextAware { + CREATE OPTIONAL PROPERTY ownSensitivity: default::Sensitivity { + CREATE ANNOTATION std::description := "A writable source of a sensitivity. This doesn't necessarily mean it be the same as .sensitivity, which is what is used for authorization."; + }; + CREATE ANNOTATION std::description := 'A type that has a project context, which allows it to be\n aware of the sensitivity & current user membership for the associated context.'; + }; + CREATE ABSTRACT TYPE default::Resource EXTENDING Mixin::Audited; + CREATE ABSTRACT TYPE default::Project EXTENDING default::Resource, Project::ContextAware, Mixin::Named, Mixin::Pinnable, Mixin::Taggable { + CREATE REQUIRED PROPERTY step: Project::Step { + SET default := (Project::Step.EarlyConversations); + }; + CREATE PROPERTY status := (Project::statusFromStep(.step)); + ALTER PROPERTY ownSensitivity { + SET default := (default::Sensitivity.High); + SET OWNED; + SET REQUIRED; + SET TYPE default::Sensitivity; + CREATE ANNOTATION std::description := 'The sensitivity of the project. This is user settable for internships and calculated for translation projects'; + }; + CREATE PROPERTY departmentId: std::str { + CREATE CONSTRAINT std::exclusive; + CREATE CONSTRAINT std::expression ON (((__subject__ > 0) AND (std::len(__subject__) = 5))); + }; + CREATE PROPERTY estimatedSubmission: cal::local_date; + CREATE PROPERTY financialReportPeriod: default::ReportPeriod; + CREATE PROPERTY financialReportReceivedAt: std::datetime; + CREATE PROPERTY mouEnd: cal::local_date; + CREATE PROPERTY initialMouEnd: cal::local_date { + SET default := (.mouEnd); + CREATE REWRITE + UPDATE + USING ((.mouEnd IF (.status = Project::Status.InDevelopment) ELSE .initialMouEnd)); + }; + CREATE PROPERTY mouStart: cal::local_date; + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + CREATE PROPERTY type := ((.__type__.name)[9:-7]); + CREATE CONSTRAINT std::expression ON ((.mouEnd >= .mouStart)); + }; + CREATE TYPE default::InternshipProject EXTENDING default::Project; + CREATE TYPE Project::Context { + CREATE MULTI LINK projects: default::Project { + ON TARGET DELETE ALLOW; + }; + CREATE ANNOTATION std::description := 'A type that holds a reference to a list of projects. This allows multiple objects to hold a reference to the same list. For example, Language & Ethnologue::Language share the same context / project list.'; + }; + ALTER TYPE Project::ContextAware { + CREATE REQUIRED LINK projectContext: Project::Context { + ON TARGET DELETE DELETE SOURCE; + }; + CREATE REQUIRED SINGLE PROPERTY sensitivity := ((std::max(.projectContext.projects.ownSensitivity) ?? (.ownSensitivity ?? default::Sensitivity.High))); + }; + ALTER TYPE default::Project { + ALTER LINK projectContext { + SET default := (INSERT + Project::Context + ); + ON SOURCE DELETE DELETE TARGET; + SET OWNED; + SET TYPE Project::Context; + }; + }; + CREATE ABSTRACT TYPE default::TranslationProject EXTENDING default::Project; + CREATE TYPE default::MomentumTranslationProject EXTENDING default::TranslationProject; + CREATE TYPE default::MultiplicationTranslationProject EXTENDING default::TranslationProject; + CREATE TYPE default::FundingAccount EXTENDING default::Resource, Mixin::Named { + CREATE REQUIRED PROPERTY accountNumber: std::int16 { + CREATE CONSTRAINT std::expression ON (((__subject__ >= 0) AND (__subject__ <= 9))); + }; + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + }; + CREATE SCALAR TYPE Location::IsoAlpha3Code EXTENDING std::str { + CREATE CONSTRAINT std::regexp('^[A-Z]{3}$'); + }; + CREATE SCALAR TYPE Location::Type EXTENDING enum; + CREATE TYPE default::Location EXTENDING default::Resource, Mixin::Named { + CREATE LINK fundingAccount: default::FundingAccount; + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + CREATE LINK defaultMarketingRegion: default::Location; + CREATE PROPERTY isoAlpha3: Location::IsoAlpha3Code { + CREATE CONSTRAINT std::exclusive; + }; + CREATE REQUIRED PROPERTY type: Location::Type; + }; + ALTER TYPE default::Project { + CREATE LINK primaryLocation: default::Location; + ALTER PROPERTY departmentId { + CREATE REWRITE + INSERT + USING ((IF ((NOT (EXISTS (.departmentId)) AND (.status <= Project::Status.Active)) AND (.step >= Project::Step.PendingFinanceConfirmation)) THEN (WITH + info := + (IF (__subject__ IS default::MultiplicationTranslationProject) THEN ( + prefix := 8, + startingOffset := 201 + ) ELSE ( + prefix := (std::assert_exists(__subject__.primaryLocation.fundingAccount, message := 'Project must have a primary location')).accountNumber, + startingOffset := 11 + )) + SELECT + std::min((std::range_unpack(std::range(((info.prefix * 10000) + info.startingOffset), ((info.prefix * 10000) + 9999))) EXCEPT (DETACHED default::Project).departmentId)) + ) ELSE .departmentId)); + CREATE REWRITE + UPDATE + USING ((IF ((NOT (EXISTS (.departmentId)) AND (.status <= Project::Status.Active)) AND (.step >= Project::Step.PendingFinanceConfirmation)) THEN (WITH + info := + (IF (__subject__ IS default::MultiplicationTranslationProject) THEN ( + prefix := 8, + startingOffset := 201 + ) ELSE ( + prefix := (std::assert_exists(__subject__.primaryLocation.fundingAccount, message := 'Project must have a primary location')).accountNumber, + startingOffset := 11 + )) + SELECT + std::min((std::range_unpack(std::range(((info.prefix * 10000) + info.startingOffset), ((info.prefix * 10000) + 9999))) EXCEPT (DETACHED default::Project).departmentId)) + ) ELSE .departmentId)); + }; + }; + CREATE FUNCTION default::date_range_get_upper(period: range) -> cal::local_date USING ((std::assert_exists(std::range_get_upper(period)) - '1 day')); + CREATE FUNCTION default::hydrate(typeName: std::str, scopedValue: std::json) -> std::str USING (typeName); + CREATE FUNCTION default::str_clean(string: std::str) -> OPTIONAL std::str USING (WITH + trimmed := + std::str_trim(string, ' \t\r\n') + SELECT + (IF (std::len(trimmed) > 0) THEN trimmed ELSE {}) + ); + ALTER TYPE Mixin::Named { + ALTER PROPERTY name { + CREATE REWRITE + INSERT + USING (default::str_clean(.name)); + CREATE REWRITE + UPDATE + USING (default::str_clean(.name)); + }; + }; + CREATE FUNCTION default::str_sortable(value: std::str) -> std::str USING (std::str_lower(std::re_replace('Ñ', 'N', std::str_trim(std::re_replace(r'[ [\]|,\-$]+', ' ', value, flags := 'g')), flags := 'g'))); + CREATE ABSTRACT TYPE File::Node EXTENDING default::Resource, Mixin::Named { + CREATE LINK parent: File::Node; + CREATE MULTI LINK parents: File::Node { + CREATE PROPERTY depth: std::int16; + }; + CREATE PROPERTY public: std::bool; + CREATE REQUIRED PROPERTY size: std::int64; + }; + ALTER TYPE Mixin::Named { + CREATE INDEX ON (default::str_sortable(.name)); + }; + CREATE TYPE File::Version EXTENDING File::Node { + CREATE REQUIRED PROPERTY mimeType: std::str; + }; + CREATE TYPE default::Directory EXTENDING File::Node { + CREATE REQUIRED PROPERTY totalFiles: std::int32 { + SET default := 0; + }; + }; + CREATE TYPE default::File EXTENDING File::Node { + CREATE REQUIRED LINK latestVersion: File::Version; + CREATE REQUIRED PROPERTY mimeType: std::str; + }; + CREATE SCALAR TYPE default::population EXTENDING std::int32 { + CREATE CONSTRAINT std::expression ON ((__subject__ >= 0)); + }; + CREATE TYPE default::Language EXTENDING default::Resource, Project::ContextAware, Mixin::Named, Mixin::Pinnable, Mixin::Taggable { + ALTER LINK projectContext { + SET default := (INSERT + Project::Context + ); + ON SOURCE DELETE DELETE TARGET; + SET OWNED; + SET TYPE Project::Context; + }; + ALTER PROPERTY ownSensitivity { + SET default := (default::Sensitivity.High); + SET OWNED; + SET REQUIRED; + SET TYPE default::Sensitivity; + CREATE ANNOTATION std::description := 'The sensitivity of the language. This is a source / user settable.'; + }; + CREATE PROPERTY populationOverride: default::population; + CREATE REQUIRED PROPERTY hasExternalFirstScripture: std::bool { + SET default := false; + }; + CREATE REQUIRED PROPERTY isDialect: std::bool { + SET default := false; + }; + CREATE REQUIRED PROPERTY isSignLanguage: std::bool { + SET default := false; + }; + CREATE REQUIRED PROPERTY leastOfThese: std::bool { + SET default := false; + }; + CREATE INDEX ON ((.name, .ownSensitivity, .leastOfThese, .isSignLanguage, .isDialect)); + CREATE MULTI LINK locations: default::Location; + CREATE REQUIRED PROPERTY displayName: std::str { + SET default := (.name); + }; + CREATE PROPERTY displayNamePronunciation: std::str; + CREATE PROPERTY leastOfTheseReason: std::str; + CREATE PROPERTY registryOfLanguageVarietiesCode: std::str { + CREATE CONSTRAINT std::exclusive; + CREATE CONSTRAINT std::regexp('^[0-9]{5}$'); + }; + CREATE PROPERTY signLanguageCode: std::str { + CREATE CONSTRAINT std::regexp(r'^[A-Z]{2}\d{2}$'); + }; + CREATE PROPERTY sponsorEstimatedEndDate: cal::local_date; + }; + ALTER TYPE default::Location { + CREATE LINK mapImage: default::File; + }; + CREATE TYPE default::SystemAgent EXTENDING default::Actor, Mixin::Named { + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + }; + CREATE GLOBAL default::currentRoles := ((GLOBAL default::currentActor).roles); + CREATE ABSTRACT TYPE Project::Child EXTENDING default::Resource, Project::ContextAware { + CREATE REQUIRED LINK project: default::Project { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + CREATE TRIGGER enforceCorrectProjectContext + AFTER UPDATE, INSERT + FOR EACH DO (std::assert((__new__.projectContext = __new__.project.projectContext), message := "Given project context must match given project's context")); + CREATE ANNOTATION std::description := 'A type that is a child of a project. It will always have a reference to a single project that it is under.'; + }; + CREATE ABSTRACT TYPE Engagement::Child EXTENDING Project::Child { + CREATE ANNOTATION std::description := 'A type that is a child of an engagement. It will always have a reference to a single engagement & project that it is under.'; + }; + CREATE ABSTRACT TYPE Engagement::Ceremony EXTENDING Engagement::Child { + CREATE PROPERTY actualDate: cal::local_date; + CREATE PROPERTY estimatedDate: cal::local_date; + CREATE REQUIRED PROPERTY planned: std::bool { + SET default := false; + }; + }; + CREATE TYPE Engagement::CertificationCeremony EXTENDING Engagement::Ceremony; + CREATE TYPE Engagement::DedicationCeremony EXTENDING Engagement::Ceremony; + CREATE SCALAR TYPE Engagement::InternPosition EXTENDING enum; + CREATE SCALAR TYPE Engagement::Status EXTENDING enum; + CREATE SCALAR TYPE Product::Methodology EXTENDING enum; + CREATE SCALAR TYPE default::RichText EXTENDING std::json; + CREATE ABSTRACT TYPE default::Engagement EXTENDING Project::Child { + CREATE PROPERTY completedDate: cal::local_date { + CREATE ANNOTATION std::description := 'Translation / Growth Plan complete date'; + }; + CREATE PROPERTY description: default::RichText; + CREATE PROPERTY disbursementCompleteDate: cal::local_date; + CREATE PROPERTY endDateOverride: cal::local_date; + CREATE PROPERTY endDate := ((.endDateOverride ?? .project.mouEnd)); + CREATE PROPERTY initialEndDate: cal::local_date; + CREATE PROPERTY lastReactivatedAt: std::datetime; + CREATE PROPERTY lastSuspendedAt: std::datetime; + CREATE PROPERTY startDateOverride: cal::local_date; + CREATE PROPERTY startDate := ((.startDateOverride ?? .project.mouStart)); + CREATE REQUIRED PROPERTY status: Engagement::Status { + SET default := (Engagement::Status.InDevelopment); + }; + CREATE PROPERTY statusModifiedAt: std::datetime { + CREATE REWRITE + UPDATE + USING ((std::datetime_of_statement() IF (.status != __old__.status) ELSE .statusModifiedAt)); + }; + ALTER PROPERTY initialEndDate { + CREATE REWRITE + INSERT + USING ((.endDate IF (.status = Engagement::Status.InDevelopment) ELSE .initialEndDate)); + CREATE REWRITE + UPDATE + USING ((.endDate IF (.status = Engagement::Status.InDevelopment) ELSE .initialEndDate)); + }; + ALTER PROPERTY lastReactivatedAt { + CREATE REWRITE + UPDATE + USING ((std::datetime_of_statement() IF (((.status != __old__.status) AND (.status = Engagement::Status.Active)) AND (__old__.status = Engagement::Status.Suspended)) ELSE .lastReactivatedAt)); + }; + ALTER PROPERTY lastSuspendedAt { + CREATE REWRITE + UPDATE + USING ((std::datetime_of_statement() IF ((.status != __old__.status) AND (.status = Engagement::Status.Suspended)) ELSE .lastSuspendedAt)); + }; + }; + CREATE TYPE default::InternshipEngagement EXTENDING default::Engagement { + ALTER LINK project { + SET OWNED; + SET REQUIRED; + SET TYPE default::InternshipProject USING ({}); + }; + CREATE LINK growthPlan: default::File; + CREATE LINK countryOfOrigin: default::Location; + CREATE MULTI PROPERTY methodologies: Product::Methodology; + CREATE PROPERTY position: Engagement::InternPosition; + }; + CREATE TYPE User::Unavailability EXTENDING default::Resource { + CREATE REQUIRED PROPERTY dates: range; + CREATE PROPERTY `end` := (std::assert_exists(std::range_get_upper(.dates))); + CREATE PROPERTY `start` := (std::assert_exists(std::range_get_lower(.dates))); + CREATE REQUIRED PROPERTY description: std::str; + }; + CREATE SCALAR TYPE User::Status EXTENDING enum; + CREATE TYPE default::User EXTENDING default::Resource, default::Actor, Mixin::Pinnable { + CREATE MULTI LINK pins: Mixin::Pinnable { + ON TARGET DELETE ALLOW; + }; + CREATE MULTI LINK unavailabilities: User::Unavailability { + ON SOURCE DELETE DELETE TARGET; + ON TARGET DELETE ALLOW; + }; + CREATE MULTI LINK locations: default::Location; + CREATE PROPERTY about: std::str; + CREATE REQUIRED PROPERTY realFirstName: std::str; + CREATE REQUIRED PROPERTY displayFirstName: std::str { + SET default := (.realFirstName); + }; + CREATE REQUIRED PROPERTY realLastName: std::str; + CREATE REQUIRED PROPERTY displayLastName: std::str { + SET default := (.realLastName); + }; + CREATE PROPERTY email: std::str { + CREATE CONSTRAINT std::exclusive; + }; + CREATE PROPERTY phone: std::str; + CREATE REQUIRED PROPERTY status: User::Status { + SET default := (User::Status.Active); + }; + CREATE REQUIRED PROPERTY timezone: std::str { + SET default := 'America/Chicago'; + }; + CREATE PROPERTY title: std::str; + }; + CREATE SCALAR TYPE User::Degree EXTENDING enum; + CREATE TYPE User::Education EXTENDING default::Resource { + CREATE REQUIRED PROPERTY degree: User::Degree; + CREATE REQUIRED PROPERTY institution: std::str; + CREATE REQUIRED PROPERTY major: std::str; + }; + CREATE GLOBAL default::currentUser := (SELECT + default::User + FILTER + (.id = GLOBAL default::currentActorId) + ); + CREATE TYPE Project::Member EXTENDING Project::Child { + CREATE REQUIRED LINK user: default::User { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + CREATE CONSTRAINT std::exclusive ON ((.project, .user)); + CREATE MULTI PROPERTY roles: default::Role; + }; + ALTER TYPE Engagement::Child { + CREATE REQUIRED LINK engagement: default::Engagement { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + }; + ALTER TYPE default::Project { + CREATE MULTI LINK members := (.{}); + }; + CREATE REQUIRED LINK language: default::Language { + SET readonly := true; + }; + CREATE TRIGGER addProjectToContextOfLanguage + AFTER INSERT + FOR EACH DO (UPDATE + __new__.language.projectContext + SET { + projects += __new__.project + }); + CREATE TRIGGER removeProjectFromContextOfLanguage + AFTER DELETE + FOR EACH DO (UPDATE + __old__.language.projectContext + SET { + projects -= __old__.project + }); + CREATE LINK pnp: default::File; + CREATE CONSTRAINT std::exclusive ON ((.project, .language)); + CREATE PROPERTY historicGoal: std::str { + CREATE ANNOTATION std::deprecated := 'Legacy data'; + }; + CREATE REQUIRED PROPERTY lukePartnership: std::bool { + SET default := false; + }; + CREATE REQUIRED PROPERTY openToInvestorVisit: std::bool { + SET default := false; + }; + CREATE PROPERTY paratextRegistryId: std::str; + CREATE PROPERTY sentPrintingDate: cal::local_date { + CREATE ANNOTATION std::deprecated := 'Legacy data'; + }; + }; + ALTER TYPE default::InternshipEngagement { + CREATE TRIGGER connectCertificationCeremony + AFTER INSERT + FOR EACH DO (INSERT + Engagement::CertificationCeremony + { + createdAt := std::datetime_of_statement(), + modifiedAt := std::datetime_of_statement(), + createdBy := std::assert_exists(GLOBAL default::currentActor), + modifiedBy := std::assert_exists(GLOBAL default::currentActor), + engagement := __new__, + project := __new__.project, + projectContext := __new__.projectContext + }); + }; + ALTER TYPE default::LanguageEngagement { + CREATE TRIGGER connectDedicationCeremony + AFTER INSERT + FOR EACH DO (INSERT + Engagement::DedicationCeremony + { + createdAt := std::datetime_of_statement(), + modifiedAt := std::datetime_of_statement(), + createdBy := std::assert_exists(GLOBAL default::currentActor), + modifiedBy := std::assert_exists(GLOBAL default::currentActor), + engagement := __new__, + project := __new__.project, + projectContext := __new__.projectContext + }); + }; + ALTER TYPE Engagement::Child { + CREATE TRIGGER enforceEngagementProject + AFTER UPDATE, INSERT + FOR EACH DO (std::assert((__new__.engagement.project = __new__.project), message := 'Given engagement must be for the same project as the given project.')); + }; + ALTER TYPE Engagement::Ceremony { + CREATE CONSTRAINT std::exclusive ON (.engagement); + }; + CREATE SCALAR TYPE Ethnologue::code EXTENDING std::str { + CREATE CONSTRAINT std::regexp('^[a-z]{3}$'); + }; + CREATE TYPE Ethnologue::Language EXTENDING Project::ContextAware { + CREATE REQUIRED LINK language: default::Language { + ON TARGET DELETE DELETE SOURCE; + CREATE CONSTRAINT std::exclusive; + }; + CREATE PROPERTY population: default::population; + CREATE PROPERTY code: Ethnologue::code { + CREATE CONSTRAINT std::exclusive; + }; + CREATE PROPERTY name: std::str; + CREATE PROPERTY provisionalCode: Ethnologue::code { + CREATE CONSTRAINT std::exclusive; + }; + }; + ALTER TYPE Mixin::Pinnable { + CREATE PROPERTY pinned := (WITH + user := + (SELECT + default::User + FILTER + (.id = GLOBAL default::currentActorId) + ) + SELECT + (__source__ IN user.pins) + ); + }; + ALTER TYPE default::Project { + CREATE LINK rootDirectory: default::Directory; + CREATE PROPERTY engagementTotal := (std::count(. 0)) OR NOT (EXISTS (__new__.primaryLocation))), message := 'Project must have a primary location with a specified funding account')); + CREATE LINK marketingLocation: default::Location; + CREATE MULTI LINK otherLocations: default::Location; + }; + ALTER TYPE default::InternshipProject { + CREATE MULTI LINK engagements := (.GLOBAL default::currentUserId).roles - SELECT - ((EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT givenRoles)) OR (.isOwner ?? false)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT givenRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'}))) - ); - }; -}; diff --git a/dbschema/migrations/00002-m1oatla.edgeql b/dbschema/migrations/00002-m1oatla.edgeql new file mode 100644 index 0000000000..e0ebc042f6 --- /dev/null +++ b/dbschema/migrations/00002-m1oatla.edgeql @@ -0,0 +1,581 @@ +CREATE MIGRATION m1oatla3xkxgpqdkclpj7rxmrcimqfnp5ikwfrdco4muttq5jrfy2a + ONTO m1zafsshfknkck6a7esom7ck425amgmeqx2pds6zimbvsajhz2ekfq +{ + CREATE MODULE Budget IF NOT EXISTS; + CREATE MODULE Media IF NOT EXISTS; + CREATE MODULE Organization IF NOT EXISTS; + CREATE MODULE Partner IF NOT EXISTS; + CREATE MODULE Partnership IF NOT EXISTS; + CREATE MODULE ProgressReport IF NOT EXISTS; + CREATE MODULE ProgressReport::Media IF NOT EXISTS; + CREATE MODULE ProgressReport::ProductProgress IF NOT EXISTS; + CREATE MODULE Prompt IF NOT EXISTS; + CREATE MODULE Scripture IF NOT EXISTS; + CREATE TYPE Budget::Record EXTENDING Project::Child { + CREATE REQUIRED PROPERTY fiscalYear: std::int16 { + SET readonly := true; + }; + CREATE PROPERTY amount: std::float32; + }; + CREATE SCALAR TYPE Budget::Status EXTENDING enum; + CREATE TYPE default::Budget EXTENDING Project::Child { + CREATE REQUIRED PROPERTY status: Budget::Status { + SET default := (Budget::Status.Pending); + }; + CREATE LINK universalTemplate: default::File; + }; + ALTER TYPE Budget::Record { + CREATE REQUIRED LINK budget: default::Budget { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + }; + CREATE SCALAR TYPE Organization::Reach EXTENDING enum; + CREATE SCALAR TYPE Organization::Type EXTENDING enum; + CREATE TYPE default::Organization EXTENDING default::Resource, Project::ContextAware, Mixin::Named { + ALTER LINK projectContext { + SET default := (INSERT + Project::Context + ); + ON SOURCE DELETE DELETE TARGET; + SET OWNED; + SET TYPE Project::Context; + }; + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + CREATE MULTI LINK locations: default::Location; + CREATE PROPERTY acronym: std::str; + CREATE PROPERTY address: std::str; + CREATE MULTI PROPERTY reach: Organization::Reach; + CREATE MULTI PROPERTY types: Organization::Type; + }; + ALTER TYPE Budget::Record { + CREATE REQUIRED LINK organization: default::Organization { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + CREATE CONSTRAINT std::exclusive ON ((.budget, .fiscalYear, .organization)); + }; + ALTER TYPE default::Budget { + CREATE LINK records := (.; + }; + CREATE TYPE Media::Image EXTENDING Media::Visual; + CREATE TYPE Media::Video EXTENDING Media::Visual, Media::Temporal; + CREATE SCALAR TYPE Product::Medium EXTENDING enum; + CREATE TYPE Product::PartnershipProducingMedium EXTENDING Engagement::Child { + CREATE REQUIRED PROPERTY medium: Product::Medium; + }; + CREATE SCALAR TYPE Partner::Type EXTENDING enum; + CREATE SCALAR TYPE Partnership::AgreementStatus EXTENDING enum; + CREATE SCALAR TYPE Partnership::FinancialReportingType EXTENDING enum; + CREATE TYPE default::Partnership EXTENDING Project::Child { + CREATE LINK agreement: default::File; + CREATE LINK mou: default::File; + CREATE PROPERTY mouEndOverride: cal::local_date; + CREATE PROPERTY mouEnd := ((.mouEndOverride ?? .project.mouEnd)); + CREATE PROPERTY mouStartOverride: cal::local_date; + CREATE PROPERTY mouStart := ((.mouStartOverride ?? .project.mouStart)); + CREATE REQUIRED PROPERTY agreementStatus: Partnership::AgreementStatus { + SET default := (Partnership::AgreementStatus.NotAttached); + }; + CREATE PROPERTY financialReportingType: Partnership::FinancialReportingType; + CREATE REQUIRED PROPERTY mouStatus: Partnership::AgreementStatus { + SET default := (Partnership::AgreementStatus.NotAttached); + }; + CREATE REQUIRED PROPERTY primary: std::bool { + SET default := false; + }; + CREATE MULTI PROPERTY types: Partner::Type; + }; + ALTER TYPE Product::PartnershipProducingMedium { + CREATE REQUIRED LINK partnership: default::Partnership { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + CREATE CONSTRAINT std::exclusive ON ((.engagement, .partnership, .medium)); + }; + CREATE ABSTRACT TYPE ProgressReport::Child EXTENDING Engagement::Child { + CREATE ANNOTATION std::description := 'A type that is a child of a progress report. It will always have a reference to a single progress report and engagement that it is under.'; + }; + CREATE ABSTRACT TYPE Prompt::PromptVariantResponse EXTENDING default::Resource, Mixin::Embedded { + CREATE PROPERTY promptId: default::nanoid; + CREATE ANNOTATION std::description := 'An instance of a prompt and the responses per variant.'; + }; + CREATE TYPE ProgressReport::CommunityStory EXTENDING ProgressReport::Child, Prompt::PromptVariantResponse; + CREATE TYPE ProgressReport::Highlight EXTENDING ProgressReport::Child, Prompt::PromptVariantResponse; + CREATE TYPE ProgressReport::Media::VariantGroup; + ALTER TYPE default::File { + CREATE SINGLE LINK media := (.latestVersion.; + CREATE ABSTRACT TYPE default::PeriodicReport EXTENDING default::Resource, Mixin::Embedded { + CREATE LINK reportFile: default::File; + CREATE REQUIRED PROPERTY period: range; + CREATE PROPERTY `end` := (default::date_range_get_upper(.period)); + CREATE PROPERTY receivedDate: cal::local_date; + CREATE PROPERTY skippedReason: std::str; + CREATE PROPERTY `start` := (std::range_get_lower(.period)); + }; + CREATE TYPE default::ProgressReport EXTENDING default::PeriodicReport, Engagement::Child { + ALTER LINK engagement { + SET OWNED; + SET TYPE default::LanguageEngagement USING ({}); + }; + ALTER LINK container { + SET OWNED; + SET TYPE default::LanguageEngagement USING ({}); + }; + }; + ALTER TYPE ProgressReport::Child { + CREATE REQUIRED LINK report: default::ProgressReport { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + CREATE TRIGGER enforceProgressReportEngagement + AFTER UPDATE, INSERT + FOR EACH DO (std::assert((__new__.report.engagement = __new__.engagement), message := 'Given progress report must be for the same engagement as the given engagement')); + }; + ALTER TYPE ProgressReport::CommunityStory { + ALTER LINK container { + SET OWNED; + SET TYPE default::ProgressReport USING ({}); + }; + }; + ALTER TYPE ProgressReport::Highlight { + ALTER LINK container { + SET OWNED; + SET TYPE default::ProgressReport USING ({}); + }; + }; + ALTER TYPE ProgressReport::TeamNews { + ALTER LINK container { + SET OWNED; + SET TYPE default::ProgressReport USING ({}); + }; + }; + ALTER TYPE ProgressReport::VarianceExplanation { + ALTER LINK report { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + }; + CREATE TYPE Prompt::VariantResponse EXTENDING Mixin::Audited { + CREATE REQUIRED LINK pvr: Prompt::PromptVariantResponse; + CREATE REQUIRED PROPERTY variant: std::str; + CREATE CONSTRAINT std::exclusive ON ((.pvr, .variant)); + CREATE ANNOTATION std::description := 'A response (for a variant) to an instance of a prompt.'; + CREATE PROPERTY response: default::RichText; + }; + ALTER TYPE Prompt::PromptVariantResponse { + CREATE LINK responses := (.; + CREATE SCALAR TYPE ProgressReport::ProductProgress::Variant EXTENDING enum; + CREATE TYPE ProgressReport::ProductProgress::Step EXTENDING Mixin::Timestamped, Project::ContextAware { + CREATE REQUIRED LINK report: default::ProgressReport; + CREATE REQUIRED PROPERTY step: Product::Step; + CREATE REQUIRED PROPERTY variant: ProgressReport::ProductProgress::Variant; + CREATE PROPERTY completed: std::float32; + }; + CREATE SCALAR TYPE Product::ProgressMeasurement EXTENDING enum; + CREATE SCALAR TYPE Product::Purpose EXTENDING enum; + CREATE ABSTRACT TYPE default::Product EXTENDING Engagement::Child { + CREATE PROPERTY describeCompletion: std::str; + CREATE MULTI PROPERTY mediums: Product::Medium; + CREATE PROPERTY methodology: Product::Methodology; + CREATE PROPERTY placeholderDescription: std::str; + CREATE PROPERTY pnpIndex: std::int16; + CREATE PROPERTY progressStepMeasurement: Product::ProgressMeasurement; + CREATE PROPERTY progressTarget: std::int16; + CREATE MULTI PROPERTY purposes: Product::Purpose; + CREATE MULTI PROPERTY steps: Product::Step; + }; + ALTER TYPE ProgressReport::ProductProgress::Step { + CREATE REQUIRED LINK product: default::Product; + CREATE CONSTRAINT std::exclusive ON ((.report, .product, .variant, .step)); + }; + CREATE SCALAR TYPE ProgressReport::ProductProgress::Period EXTENDING enum; + CREATE TYPE ProgressReport::ProductProgress::Summary { + CREATE REQUIRED LINK report: default::ProgressReport; + CREATE REQUIRED PROPERTY period: ProgressReport::ProductProgress::Period; + CREATE CONSTRAINT std::exclusive ON ((.report, .period)); + CREATE REQUIRED PROPERTY actual: std::float32; + CREATE REQUIRED PROPERTY planned: std::float32; + }; + CREATE TYPE Project::WorkflowEvent EXTENDING Project::ContextAware { + CREATE REQUIRED LINK project: default::Project { + ON TARGET DELETE DELETE SOURCE; + SET readonly := true; + }; + CREATE REQUIRED PROPERTY at: std::datetime { + SET default := (std::datetime_of_statement()); + SET readonly := true; + }; + CREATE REQUIRED PROPERTY to: Project::Step { + SET readonly := true; + }; + CREATE REQUIRED LINK who: default::Actor { + SET default := (GLOBAL default::currentActor); + SET readonly := true; + }; + CREATE PROPERTY notes: default::RichText { + SET readonly := true; + }; + CREATE PROPERTY transitionKey: std::uuid { + SET readonly := true; + }; + }; + ALTER TYPE default::Project { + CREATE LINK workflowEvents := (.= 0) AND (__subject__ <= 31101))); + }; + CREATE REQUIRED PROPERTY book: std::str { + SET readonly := true; + }; + CREATE REQUIRED PROPERTY chapter: std::int16 { + SET readonly := true; + CREATE CONSTRAINT std::expression ON (((__subject__ >= 1) AND (__subject__ <= 150))); + }; + CREATE REQUIRED PROPERTY verse: std::int16 { + SET readonly := true; + CREATE CONSTRAINT std::expression ON (((__subject__ >= 1) AND (__subject__ <= 176))); + }; + CREATE PROPERTY label := (((((.book ++ ' ') ++ .chapter) ++ ':') ++ .verse)); + }; + ALTER TYPE Scripture::VerseRange { + CREATE REQUIRED LINK `end`: Scripture::Verse { + ON SOURCE DELETE DELETE TARGET; + SET readonly := true; + }; + CREATE REQUIRED LINK `start`: Scripture::Verse { + ON SOURCE DELETE DELETE TARGET; + SET readonly := true; + }; + CREATE PROPERTY ids := (std::range(.`start`.verseId, .`end`.verseId, inc_upper := true)); + }; + ALTER TYPE Scripture::Collection { + CREATE PROPERTY ids := (std::multirange(std::array_agg(.verses.ids))); + }; + ALTER TYPE default::DerivativeScriptureProduct { + ALTER LINK scripture { + CREATE REWRITE + INSERT + USING ((IF EXISTS (.scriptureOverride) THEN (IF EXISTS (.scriptureOverride.verses) THEN .scriptureOverride ELSE {}) ELSE .produces.scripture)); + CREATE REWRITE + UPDATE + USING ((IF EXISTS (.scriptureOverride) THEN (IF EXISTS (.scriptureOverride.verses) THEN .scriptureOverride ELSE {}) ELSE .produces.scripture)); + }; + }; + ALTER TYPE default::Product { + CREATE TRIGGER denyEmptyScriptureCollection + AFTER UPDATE, INSERT + FOR EACH DO (std::assert((NOT (EXISTS (__new__.scripture)) OR EXISTS (__new__.scripture.verses)), message := '`Product.scripture` should have a `Scripture::Collection` with verses or be null/empty-set')); + }; + ALTER TYPE default::Producible { + CREATE TRIGGER denyEmptyScriptureCollection + AFTER UPDATE, INSERT + FOR EACH DO (std::assert((NOT (EXISTS (__new__.scripture)) OR EXISTS (__new__.scripture.verses)), message := '`Producible.scripture` should have a `Scripture::Collection` with verses or be null/empty-set')); + }; + CREATE TYPE default::OtherProduct EXTENDING default::Product { + CREATE PROPERTY description: std::str; + CREATE REQUIRED PROPERTY title: std::str; + }; + CREATE TYPE default::FieldRegion EXTENDING default::Resource, Mixin::Named { + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + CREATE REQUIRED LINK director: default::User; + }; + CREATE TYPE default::FieldZone EXTENDING default::Resource, Mixin::Named { + ALTER PROPERTY name { + SET OWNED; + CREATE CONSTRAINT std::exclusive; + }; + CREATE REQUIRED LINK director: default::User; + }; + ALTER TYPE default::FieldRegion { + CREATE REQUIRED LINK fieldZone: default::FieldZone; + }; + ALTER TYPE default::FieldZone { + CREATE LINK fieldRegions := (.{}); + }; + }; + CREATE TYPE default::NarrativeReport EXTENDING default::PeriodicReport, Project::Child { + ALTER LINK container { + SET OWNED; + SET TYPE default::Project USING ({}); + }; + }; + ALTER TYPE default::Partnership { + CREATE REQUIRED LINK partner: default::Partner; + CREATE LINK organization := (.partner.organization); + CREATE CONSTRAINT std::exclusive ON ((.project, .partner)); + }; +}; diff --git a/dbschema/migrations/00003-m1nrdwc.edgeql b/dbschema/migrations/00003-m1nrdwc.edgeql deleted file mode 100644 index e1679e1694..0000000000 --- a/dbschema/migrations/00003-m1nrdwc.edgeql +++ /dev/null @@ -1,52 +0,0 @@ -CREATE MIGRATION m1nrdwc3vdx7w2iui6bxlxxe3urjirypmaw5ft33yehtq33dgeyovq - ONTO m1jtglkzb7fc5jezkxnximpq4uqqykdyevfvsnkzf26fv4ouxv274a -{ - ALTER TYPE default::Project { - ALTER PROPERTY departmentId { - DROP REWRITE - INSERT ; - }; - }; - ALTER TYPE default::Project { - ALTER PROPERTY departmentId { - CREATE REWRITE - INSERT - USING ((IF ((NOT (EXISTS (.departmentId)) AND (.status <= Project::Status.Active)) AND (.step >= Project::Step.PendingFinanceConfirmation)) THEN (WITH - info := - (IF (__subject__ IS default::MultiplicationTranslationProject) THEN ( - prefix := 8, - startingOffset := 201 - ) ELSE ( - prefix := (std::assert_exists(__subject__.primaryLocation.fundingAccount, message := 'Project must have a primary location')).accountNumber, - startingOffset := 11 - )) - SELECT - std::min((std::range_unpack(std::range(((info.prefix * 10000) + info.startingOffset), ((info.prefix * 10000) + 9999))) EXCEPT (DETACHED default::Project).departmentId)) - ) ELSE .departmentId)); - }; - }; - ALTER TYPE default::Project { - ALTER PROPERTY departmentId { - DROP REWRITE - UPDATE ; - }; - }; - ALTER TYPE default::Project { - ALTER PROPERTY departmentId { - CREATE REWRITE - UPDATE - USING ((IF ((NOT (EXISTS (.departmentId)) AND (.status <= Project::Status.Active)) AND (.step >= Project::Step.PendingFinanceConfirmation)) THEN (WITH - info := - (IF (__subject__ IS default::MultiplicationTranslationProject) THEN ( - prefix := 8, - startingOffset := 201 - ) ELSE ( - prefix := (std::assert_exists(__subject__.primaryLocation.fundingAccount, message := 'Project must have a primary location')).accountNumber, - startingOffset := 11 - )) - SELECT - std::min((std::range_unpack(std::range(((info.prefix * 10000) + info.startingOffset), ((info.prefix * 10000) + 9999))) EXCEPT (DETACHED default::Project).departmentId)) - ) ELSE .departmentId)); - }; - }; -}; diff --git a/dbschema/migrations/00003-m1zasar.edgeql b/dbschema/migrations/00003-m1zasar.edgeql new file mode 100644 index 0000000000..fe8fab6883 --- /dev/null +++ b/dbschema/migrations/00003-m1zasar.edgeql @@ -0,0 +1,64 @@ +CREATE MIGRATION m1zasaruu7gg245gjtxbbvqt5t4mh5c2zf5qvq5bvyihhw37uns3ta + ONTO m1oatla3xkxgpqdkclpj7rxmrcimqfnp5ikwfrdco4muttq5jrfy2a +{ + CREATE MODULE Comments IF NOT EXISTS; + CREATE MODULE Post IF NOT EXISTS; + CREATE ABSTRACT TYPE Comments::Aware EXTENDING default::Resource; + CREATE ABSTRACT TYPE Mixin::Postable EXTENDING default::Resource; + ALTER TYPE default::Project EXTENDING Mixin::Postable, + Comments::Aware BEFORE default::Resource; + CREATE TYPE Comments::Thread EXTENDING default::Resource, Mixin::Embedded { + ALTER LINK container { + SET SINGLE; + ON TARGET DELETE DELETE SOURCE; + SET OWNED; + SET REQUIRED; + SET TYPE Comments::Aware USING ({}); + }; + }; + ALTER TYPE Comments::Aware { + CREATE LINK commentThreads := (.; + CREATE SCALAR TYPE Post::Type EXTENDING enum; + CREATE TYPE default::Post EXTENDING default::Resource, Mixin::Embedded { + ALTER LINK container { + SET SINGLE; + ON TARGET DELETE DELETE SOURCE; + SET OWNED; + SET REQUIRED; + SET TYPE Mixin::Postable USING ({}); + }; + CREATE SINGLE PROPERTY isMember := (.container[IS Project::ContextAware].isMember); + CREATE SINGLE PROPERTY sensitivity := (.container[IS Project::ContextAware].sensitivity); + CREATE REQUIRED PROPERTY body: default::RichText; + CREATE REQUIRED PROPERTY shareability: Post::Shareability; + CREATE REQUIRED PROPERTY type: Post::Type; + }; + ALTER TYPE Mixin::Postable { + CREATE LINK posts := (. USING ( - std::range( - std::to_datetime(std::range_get_lower(.dates), 'America/Chicago'), - std::to_datetime(std::range_get_upper(.dates), 'America/Chicago') - ) - ); - }; - }; - ALTER TYPE default::Language { - CREATE MULTI LINK locations: default::Location; - }; - ALTER TYPE default::Organization { - CREATE MULTI LINK locations: default::Location; - }; - ALTER TYPE default::User { - ALTER LINK education { - ON SOURCE DELETE DELETE TARGET; - }; - CREATE MULTI LINK locations: default::Location; - ALTER LINK unavailabilities { - ON SOURCE DELETE DELETE TARGET; - }; - }; -}; diff --git a/dbschema/migrations/00004-m1yphrx.edgeql b/dbschema/migrations/00004-m1yphrx.edgeql new file mode 100644 index 0000000000..81661cfd95 --- /dev/null +++ b/dbschema/migrations/00004-m1yphrx.edgeql @@ -0,0 +1,343 @@ +CREATE MIGRATION m1yphrx7buk7mltbmsuabovma34ukfn7dkllc3pfggljlkadasw2gq + ONTO m1zasaruu7gg245gjtxbbvqt5t4mh5c2zf5qvq5bvyihhw37uns3ta +{ + ALTER TYPE Budget::Record { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForBudgetRecord + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium))))); + CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudgetRecord + ALLOW UPDATE WRITE, DELETE, INSERT ; + }; + ALTER TYPE Comments::Aware { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForCommentable + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCommentable + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForCommentable + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Comments::Comment { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForComment + ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForComment + ALLOW INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForComment + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles)) OR .isCreator)); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForComment + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Comments::Thread { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForCommentThread + ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForCommentThread + ALLOW INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCommentThread + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles)) OR .isCreator)); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForCommentThread + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Engagement::Ceremony { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCeremony + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForCeremony + ALLOW UPDATE WRITE, DELETE, INSERT ; + }; + ALTER TYPE Ethnologue::Language { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForEthnologueLanguage + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEthnologueLanguage + ALLOW SELECT, UPDATE READ USING (((((EXISTS (({'Administrator', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.sensitivity <= default::Sensitivity.Medium))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR ((default::Role.Fundraising IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Marketing', 'Fundraising', 'ExperienceOperations'} INTERSECT GLOBAL default::currentRoles)) AND (.sensitivity <= default::Sensitivity.Low)))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEthnologueLanguage + ALLOW UPDATE WRITE ; + }; + ALTER TYPE File::Node { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFileNode + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFileNode + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFileNode + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Directory { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForDirectory + ALLOW SELECT, UPDATE READ USING (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles))); + }; + ALTER TYPE default::Media { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForMedia + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForMedia + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForMedia + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Mixin::Postable { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForPostable + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPostable + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPostable + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Project { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProject + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProject + ALLOW INSERT USING (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProject + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Fundraising', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'RegionalDirector', 'FieldOperationsDirector', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProject + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Language { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForLanguage + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForLanguage + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Fundraising', 'Marketing', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForLanguage + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Partner { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForPartner + ALLOW DELETE USING (EXISTS (({'Administrator', 'Controller'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForPartner + ALLOW INSERT USING (EXISTS (({'Administrator', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPartner + ALLOW SELECT, UPDATE READ USING (((((EXISTS (({'Administrator', 'ConsultantManager', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ExperienceOperations', 'Fundraising'} INTERSECT GLOBAL default::currentRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND ((.isMember AND (.sensitivity <= default::Sensitivity.Medium)) OR (.sensitivity <= default::Sensitivity.Low)))) OR ((default::Role.StaffMember IN GLOBAL default::currentRoles) AND (.sensitivity <= default::Sensitivity.Low)))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPartner + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::CommunityStory { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportCommunityStory + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportCommunityStory + ALLOW INSERT USING ((EXISTS (({'Administrator', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportCommunityStory + ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportCommunityStory + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::Highlight { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportHighlight + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportHighlight + ALLOW INSERT USING ((EXISTS (({'Administrator', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportHighlight + ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportHighlight + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::Media { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportMedia + ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportMedia + ALLOW INSERT USING ((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR (((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'draft'))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND (.variant = 'published'))) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.variant IN {'draft', 'translated', 'fpm'}))) OR (((default::Role.Translator IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'translated')))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportMedia + ALLOW SELECT, UPDATE READ USING (((((((EXISTS (({'Administrator', 'Leadership', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'draft'))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND (((.isMember AND (.variant IN {'draft', 'translated', 'fpm'})) OR ((.sensitivity <= default::Sensitivity.Low) AND (.variant IN {'fpm', 'published'}))) OR .isMember))) OR ((default::Role.Translator IN GLOBAL default::currentRoles) AND ((.isMember AND (.variant = 'translated')) OR .isMember))) OR .isCreator)); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportMedia + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::TeamNews { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportTeamNews + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportTeamNews + ALLOW INSERT USING ((EXISTS (({'Administrator', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportTeamNews + ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportTeamNews + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::VarianceExplanation { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForProgressReportVarianceExplanation + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportVarianceExplanation + ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportVarianceExplanation + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::WorkflowEvent { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportWorkflowEvent + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportWorkflowEvent + ALLOW INSERT USING ((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND ((.transitionId IN {'5da76b5163', 'cb18f58cbf', '651d2a4dcc', 'e14c52346b'}) ?? false))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND ((.transitionId = '2d88e3cd6e') ?? false))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionId IN {'5da76b5163', 'cb18f58cbf', '651d2a4dcc', '580377ea2b', '0d854e832e', 'e14c52346b', '2b137bcd66', 'a0c0c48a8c', 'e3e11c86b9'}) ?? false))) OR ((default::Role.Translator IN GLOBAL default::currentRoles) AND ((.transitionId IN {'580377ea2b', '0d854e832e'}) ?? false)))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportWorkflowEvent + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportWorkflowEvent + ALLOW UPDATE WRITE ; + }; + ALTER TYPE ProgressReport::ProductProgress::Step { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForStepProgress + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForStepProgress + ALLOW SELECT, UPDATE READ USING (((((EXISTS (({'Administrator', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'partner'))) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.variant IN {'official', 'partner'}))) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForStepProgress + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Project::FinancialApprover { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFinancialApprover + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFinancialApprover + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFinancialApprover + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Project::Member { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForProjectMember + ALLOW DELETE, INSERT USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProjectMember + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProjectMember + ALLOW UPDATE WRITE ; + }; + ALTER TYPE Project::WorkflowEvent { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProjectWorkflowEvent + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProjectWorkflowEvent + ALLOW INSERT USING ((((((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false))) OR ((EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND ((.transitionKey IN {'1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false))) OR ((default::Role.Controller IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'3c9662f5-db67-5403-b416-7aeeef0fb350', 'a050d4e1-b446-52ab-b6c9-1ab045aa91f5', 'ffff1e49-94ca-5601-ada5-6cc52c93d517', '78af1187-552f-5f7a-b6ec-c737d300aaa9', 'b65b9db4-d5b3-5557-9c1e-a25fc9edc538', '0d9f9039-7099-5faf-9b08-099d47a5cc42', 'a1088f2c-478c-5512-8a60-e4feff5538cc', 'c4663d2d-8b6d-5f45-8fb3-cb71e34555a0', 'ce663888-ca28-55cd-9e9b-1ea5b75e76b2'}) ?? false))) OR ((default::Role.FieldOperationsDirector IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'a03d96d7-bc75-51d4-b793-88aa02e26cfc', '2f012ffe-893f-52ea-b9f0-39f313b0dd1f', 'aa718b6c-8f16-589c-a0d4-50d5555534d1'}) ?? false))) OR (EXISTS (({'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'f48d59a0-67e7-57e2-9f7c-8f9cd8c3c01c', '857c7594-8af8-524e-a61f-15b6ac2bac7d', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa'}) ?? false))) OR ((EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND ((.transitionKey IN {'f48d59a0-67e7-57e2-9f7c-8f9cd8c3c01c', '857c7594-8af8-524e-a61f-15b6ac2bac7d', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa'}) ?? false))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((((.project.type = 'MomentumTranslation') AND .isMember) AND ((.transitionKey IN {'1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false)) OR (.isMember AND ((.transitionKey IN {'6ce4c5d0-5034-55c1-bf80-bf5992fe38bf', '37e42c6d-260b-51c7-98c3-d918ba057480', '4edf8b35-b462-590d-bc2c-fd0d20b46a6d', '06c4afc9-2349-50ae-ae4b-a506f5b80dee', '9d2c13e3-d73f-58bc-b68d-c447f0caa91d', 'afe0b316-57ef-5640-a098-4fa1cf0a81b9', '356c7be5-08f5-5f36-a8ad-dfc5509a872e', '3c5607fb-1bba-5c02-a7de-24e4bcf2f27b', 'aa17add1-15eb-5054-80be-809480f8b0eb', '4d495617-2fa4-5dc3-a5ec-63ae21731f1c', '2dc45f2c-c7c8-5f60-9f8c-b9994fcd6b82', '45100e48-16f0-5ac4-a0cb-7e8214414bf8', '04556e06-0efe-5bad-b7a7-9b2524e9c9cf', 'c85a70bb-ff30-5540-8c75-f0c543609418', '479ce997-0cb6-5c38-af9b-a36d3ea24e82', '792e3e28-8237-53a3-893b-e6d4e5476265', '60483d90-c54f-5dd4-b233-b53287ba4324', '7f9b7302-3ea4-5b77-b9ac-7191d11adb94', '006aebaa-83df-5225-add8-062c498191fe', '77ba67dd-b2c3-5e7b-9f33-f55c09279f3e', '0c610243-ba8f-565c-ab17-1b62e1fa6d06', '2537d694-d712-5d3f-8c8a-b07002265e39', '2267fe55-7a97-5aa1-bbc1-403e59f71f9d', 'fe0ec249-f6d6-5ce7-97c4-c19720a057cd', '758a333a-7484-5fb4-aa4c-9c620f542551', '2063137a-7901-5185-b1f8-fe76c56f5c67', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa'}) ?? false))))) OR (EXISTS (({'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'3e687176-4f02-5f2e-ad5a-374f39504034', '60e21d6d-d4e0-5103-8eee-4f4ff80404b1', '2126603a-13bc-5356-a062-d11647c0f5f6', 'd22e15d9-c4cb-564d-b429-8cef0d482e14', 'b412a3de-d8b9-5068-9e90-e6ead0b4407c', '61b12ecf-6024-54eb-9f49-83b1dc6769f1', '9c8e953f-eecd-5a42-a806-e0e3187dda66', '99dcddcb-4351-5643-a196-9fd93fe68760', 'd5ead4fc-5d99-5a82-8c32-8640ffe26162', 'aa4a45d0-d611-56f2-bd90-d47fc4626d60', 'd9aaa5d1-da74-5042-915f-e7726367ea4e', 'bcfc9f9b-c6d6-59c3-8449-42620f806211', 'f1694429-c062-5895-bff1-12e73cf8eea5', '90fb9642-7b38-510c-bc45-84e722c145b6', 'dae01c7f-13d4-5246-a3f6-2fa3e067b505', 'f7511418-bce7-5901-b141-06a470955a86', '3e19a59f-a414-5c58-a1ff-7bfc6cff7eef', '6b08ebc1-5279-58fc-b2c5-26147b278426', '05d1a69d-e717-5ca9-b011-688cf58a924f', '6ce4c5d0-5034-55c1-bf80-bf5992fe38bf', '37e42c6d-260b-51c7-98c3-d918ba057480', '4edf8b35-b462-590d-bc2c-fd0d20b46a6d', '06c4afc9-2349-50ae-ae4b-a506f5b80dee', '9d2c13e3-d73f-58bc-b68d-c447f0caa91d', 'afe0b316-57ef-5640-a098-4fa1cf0a81b9', '356c7be5-08f5-5f36-a8ad-dfc5509a872e', '3c5607fb-1bba-5c02-a7de-24e4bcf2f27b', 'aa17add1-15eb-5054-80be-809480f8b0eb', '4d495617-2fa4-5dc3-a5ec-63ae21731f1c', '2dc45f2c-c7c8-5f60-9f8c-b9994fcd6b82', '45100e48-16f0-5ac4-a0cb-7e8214414bf8', '04556e06-0efe-5bad-b7a7-9b2524e9c9cf', 'c85a70bb-ff30-5540-8c75-f0c543609418', '479ce997-0cb6-5c38-af9b-a36d3ea24e82', '792e3e28-8237-53a3-893b-e6d4e5476265', '60483d90-c54f-5dd4-b233-b53287ba4324', '7f9b7302-3ea4-5b77-b9ac-7191d11adb94', '006aebaa-83df-5225-add8-062c498191fe', '77ba67dd-b2c3-5e7b-9f33-f55c09279f3e', '0c610243-ba8f-565c-ab17-1b62e1fa6d06', '2537d694-d712-5d3f-8c8a-b07002265e39', '2267fe55-7a97-5aa1-bbc1-403e59f71f9d', 'fe0ec249-f6d6-5ce7-97c4-c19720a057cd', '758a333a-7484-5fb4-aa4c-9c620f542551', '2063137a-7901-5185-b1f8-fe76c56f5c67', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa', '1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false)))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProjectWorkflowEvent + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'Controller', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProjectWorkflowEvent + ALLOW UPDATE WRITE ; + }; + ALTER TYPE User::Education { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForEducation + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForEducation + ALLOW INSERT USING (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEducation + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'ConsultantManager', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEducation + ALLOW UPDATE WRITE ; + }; + ALTER TYPE User::Unavailability { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForUnavailability + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForUnavailability + ALLOW INSERT USING (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForUnavailability + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT GLOBAL default::currentRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'})))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForUnavailability + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Budget { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForBudget + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium))))); + CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudget + ALLOW UPDATE WRITE, DELETE, INSERT ; + }; + ALTER TYPE default::Engagement { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForEngagement + ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.status = 'InDevelopment')))); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForEngagement + ALLOW INSERT USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.project.status = 'InDevelopment')))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEngagement + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEngagement + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::LanguageEngagement { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForLanguageEngagement + ALLOW SELECT, UPDATE READ USING ((default::Role.ConsultantManager IN GLOBAL default::currentRoles)); + }; + ALTER TYPE default::Producible { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProducible + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProducible + ALLOW INSERT USING (EXISTS (({'Administrator', 'FieldOperationsDirector', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForProducible + ALLOW SELECT, UPDATE ; + }; + ALTER TYPE default::FieldRegion { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFieldRegion + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFieldRegion + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFieldRegion + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::FieldZone { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFieldZone + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFieldZone + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFieldZone + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::PeriodicReport { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPeriodicReport + ALLOW SELECT, UPDATE READ USING (WITH + isMember := + (.container[IS Project::ContextAware].isMember ?? false) + , + sensitivity := + (.container[IS Project::ContextAware].sensitivity ?? default::Sensitivity.High) + SELECT + ((EXISTS (({'Administrator', 'ExperienceOperations', 'FieldOperationsDirector', 'FieldPartner', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'ConsultantManager', 'Marketing', 'Fundraising', 'ExperienceOperations'} INTERSECT GLOBAL default::currentRoles)) AND (isMember OR (sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND isMember)) + ); + CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForPeriodicReport + ALLOW UPDATE WRITE, DELETE, INSERT ; + }; + ALTER TYPE default::FundingAccount { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFundingAccount + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFundingAccount + ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'ConsultantManager', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFundingAccount + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Location { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForLocation + ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForLocation + ALLOW SELECT, UPDATE ; + }; + ALTER TYPE default::NarrativeReport { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForNarrativeReport + ALLOW SELECT, UPDATE READ USING (WITH + isMember := + (.container[IS Project::ContextAware].isMember ?? false) + SELECT + (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND isMember) + ); + }; + ALTER TYPE default::Organization { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForOrganization + ALLOW DELETE USING (EXISTS (({'Administrator', 'Controller'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForOrganization + ALLOW INSERT USING (EXISTS (({'Administrator', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForOrganization + ALLOW SELECT, UPDATE READ USING ((((EXISTS (({'Administrator', 'ConsultantManager', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ExperienceOperations', 'Fundraising'} INTERSECT GLOBAL default::currentRoles)) AND (.sensitivity <= default::Sensitivity.Medium))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Low))))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForOrganization + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::Partnership { + CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForPartnership + ALLOW DELETE, INSERT USING (((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium))))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPartnership + ALLOW SELECT, UPDATE READ USING ((((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'Leadership', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR ((default::Role.StaffMember IN GLOBAL default::currentRoles) AND (.sensitivity <= default::Sensitivity.Low)))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPartnership + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::ProgressReport { + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReport + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)); + }; + ALTER TYPE default::Post { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForPost + ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForPost + ALLOW INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPost + ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles)) OR .isCreator)); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPost + ALLOW UPDATE WRITE ; + }; + ALTER TYPE default::User { + CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForUser + ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); + CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForUser + ALLOW INSERT USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); + CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForUser + ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (.id ?= GLOBAL default::currentActorId)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT GLOBAL default::currentRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'})))); + CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForUser + ALLOW UPDATE WRITE ; + }; +}; diff --git a/dbschema/migrations/00005-m1jmb5p.edgeql b/dbschema/migrations/00005-m1jmb5p.edgeql deleted file mode 100644 index f8a798763c..0000000000 --- a/dbschema/migrations/00005-m1jmb5p.edgeql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE MIGRATION m1jmb5p3ypawyyn6yvctr5of2zp2mw5rd5iyzrxnqmn75eyp3viroq - ONTO m1pqnnddxdh5r2mz2c2t467qes4mbrhtnttrvyejcigcxzwpjwmdka -{ - ALTER TYPE User::Unavailability { - CREATE PROPERTY `end` := (std::assert_exists(std::range_get_upper(.dates))); - CREATE PROPERTY `start` := (std::assert_exists(std::range_get_lower(.dates))); - }; -}; diff --git a/dbschema/migrations/00006-m1rxhi7.edgeql b/dbschema/migrations/00006-m1rxhi7.edgeql deleted file mode 100644 index bb953ce6a5..0000000000 --- a/dbschema/migrations/00006-m1rxhi7.edgeql +++ /dev/null @@ -1,687 +0,0 @@ -create migration m1rxhi72zd43b4hwanwavpsfx7yo5vfngfr4ngmxq4thpuvi3t45qa - onto m1jmb5p3ypawyyn6yvctr5of2zp2mw5rd5iyzrxnqmn75eyp3viroq -{ - # Drop all APs temporarily - alter type Budget::Record { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForBudgetRecord; - drop access policy CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudgetRecord; - }; - alter type Comments::Aware { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForCommentable; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForCommentable; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForCommentable; - }; - alter type Comments::Comment { - drop access policy CanDeleteGeneratedFromAppPoliciesForComment; - drop access policy CanInsertGeneratedFromAppPoliciesForComment; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForComment; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForComment; - }; - alter type Comments::Thread { - drop access policy CanDeleteGeneratedFromAppPoliciesForCommentThread; - drop access policy CanInsertGeneratedFromAppPoliciesForCommentThread; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForCommentThread; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForCommentThread; - }; - alter type Engagement::Ceremony { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForCeremony; - drop access policy CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForCeremony; - }; - alter type Ethnologue::Language { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForEthnologueLanguage; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForEthnologueLanguage; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForEthnologueLanguage; - }; - alter type File::Node { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForFileNode; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForFileNode; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForFileNode; - }; - alter type default::Media { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForMedia; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForMedia; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForMedia; - }; - alter type Mixin::Postable { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForPostable; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForPostable; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForPostable; - }; - alter type ProgressReport::CommunityStory { - drop access policy CanDeleteGeneratedFromAppPoliciesForProgressReportCommunityStory; - drop access policy CanInsertGeneratedFromAppPoliciesForProgressReportCommunityStory; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportCommunityStory; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProgressReportCommunityStory; - }; - alter type ProgressReport::Highlight { - drop access policy CanDeleteGeneratedFromAppPoliciesForProgressReportHighlight; - drop access policy CanInsertGeneratedFromAppPoliciesForProgressReportHighlight; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportHighlight; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProgressReportHighlight; - }; - alter type ProgressReport::Media { - drop access policy CanDeleteGeneratedFromAppPoliciesForProgressReportMedia; - drop access policy CanInsertGeneratedFromAppPoliciesForProgressReportMedia; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportMedia; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProgressReportMedia; - }; - alter type ProgressReport::TeamNews { - drop access policy CanDeleteGeneratedFromAppPoliciesForProgressReportTeamNews; - drop access policy CanInsertGeneratedFromAppPoliciesForProgressReportTeamNews; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportTeamNews; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProgressReportTeamNews; - }; - alter type ProgressReport::VarianceExplanation { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForProgressReportVarianceExplanation; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportVarianceExplanation; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProgressReportVarianceExplanation; - }; - alter type ProgressReport::WorkflowEvent { - drop access policy CanDeleteGeneratedFromAppPoliciesForProgressReportWorkflowEvent; - drop access policy CanInsertGeneratedFromAppPoliciesForProgressReportWorkflowEvent; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportWorkflowEvent; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProgressReportWorkflowEvent; - }; - alter type ProgressReport::ProductProgress::Step { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForStepProgress; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForStepProgress; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForStepProgress; - }; - alter type Project::Member { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForProjectMember; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProjectMember; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProjectMember; - }; - alter type User::Education { - drop access policy CanDeleteGeneratedFromAppPoliciesForEducation; - drop access policy CanInsertGeneratedFromAppPoliciesForEducation; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForEducation; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForEducation; - }; - alter type User::Unavailability { - drop access policy CanDeleteGeneratedFromAppPoliciesForUnavailability; - drop access policy CanInsertGeneratedFromAppPoliciesForUnavailability; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForUnavailability; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForUnavailability; - }; - alter type default::Budget { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForBudget; - drop access policy CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudget; - }; - alter type default::Directory { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForDirectory; - }; - alter type default::Engagement { - drop access policy CanDeleteGeneratedFromAppPoliciesForEngagement; - drop access policy CanInsertGeneratedFromAppPoliciesForEngagement; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForEngagement; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForEngagement; - }; - alter type default::Producible { - drop access policy CanDeleteGeneratedFromAppPoliciesForProducible; - drop access policy CanInsertGeneratedFromAppPoliciesForProducible; - drop access policy CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForProducible; - }; - alter type default::FieldRegion { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForFieldRegion; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForFieldRegion; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForFieldRegion; - }; - alter type default::FieldZone { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForFieldZone; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForFieldZone; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForFieldZone; - }; - alter type default::PeriodicReport { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForPeriodicReport; - drop access policy CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForPeriodicReport; - }; - alter type default::FundingAccount { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForFundingAccount; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForFundingAccount; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForFundingAccount; - }; - alter type default::Project { - drop access policy CanDeleteGeneratedFromAppPoliciesForProject; - drop access policy CanInsertGeneratedFromAppPoliciesForProject; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProject; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForProject; - }; - alter type default::Language { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForLanguage; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForLanguage; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForLanguage; - }; - alter type default::LanguageEngagement { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForLanguageEngagement; - }; - alter type default::Location { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForLocation; - drop access policy CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForLocation; - }; - alter type default::NarrativeReport { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForNarrativeReport; - }; - alter type default::Organization { - drop access policy CanDeleteGeneratedFromAppPoliciesForOrganization; - drop access policy CanInsertGeneratedFromAppPoliciesForOrganization; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForOrganization; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForOrganization; - }; - alter type default::Partner { - drop access policy CanDeleteGeneratedFromAppPoliciesForPartner; - drop access policy CanInsertGeneratedFromAppPoliciesForPartner; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForPartner; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForPartner; - }; - alter type default::Partnership { - drop access policy CanInsertDeleteGeneratedFromAppPoliciesForPartnership; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForPartnership; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForPartnership; - }; - alter type default::Post { - drop access policy CanDeleteGeneratedFromAppPoliciesForPost; - drop access policy CanInsertGeneratedFromAppPoliciesForPost; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForPost; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForPost; - }; - alter type default::ProgressReport { - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReport; - }; - alter type default::User { - drop access policy CanDeleteGeneratedFromAppPoliciesForUser; - drop access policy CanInsertGeneratedFromAppPoliciesForUser; - drop access policy CanSelectUpdateReadGeneratedFromAppPoliciesForUser; - drop access policy CanUpdateWriteGeneratedFromAppPoliciesForUser; - }; - - # Introduce Actor / SystemAgents - create abstract type default::Actor { - create multi property roles: default::Role; - }; - alter type default::User { - extending default::Actor before Mixin::Pinnable; - alter property roles { - reset cardinality; - drop owned; - reset type; - }; - }; - create type default::SystemAgent extending default::Actor, Mixin::Named { - alter property name { - set owned; - create constraint std::exclusive; - }; - }; - insert SystemAgent { name := "Ghost" }; - insert SystemAgent { name := "Anonymous" }; - insert SystemAgent { name := "External Mailing Group", roles := Role.Leadership }; - - # Stub Audited - create abstract type Mixin::Audited extending Mixin::Timestamped; - - create global default::currentActor := (select Actor filter .id = global currentUserId); - - # Switch to extend Resource & Audited - alter type default::Resource { - extending Mixin::Audited before Mixin::Timestamped; - }; - alter type default::Resource { - drop extending Mixin::Timestamped; - }; - alter type Prompt::PromptVariantResponse { - drop extending Mixin::Timestamped; - extending default::Resource before Mixin::Embedded; - }; - alter type Prompt::VariantResponse { - drop extending Mixin::Timestamped; - extending Mixin::Audited; - }; - - # Drop audited fields that will be inherited - alter type File::Node { - drop link createdBy; - drop link modifiedBy; - }; - - # Add createdBy/modifiedBy to Audited, back-fill from owner - alter type Mixin::Audited { - create required link createdBy: default::Actor { - set default := global default::currentActor; - set required using (assert_single( - Mixin::Audited[is Mixin::Owned].owner - ?? assert_exists((select default::SystemAgent filter .name = 'Ghost')) - )); - set readonly := true; - }; - create required link modifiedBy: default::Actor { - set default := global default::currentActor; - set required using (.createdBy); - create rewrite update using (global default::currentActor); - }; - }; - - # Drop Owned - alter type Mixin::Owned { - drop property isOwner; - drop link owner; - }; - alter type default::User drop extending Mixin::Owned; - alter type Comments::Comment drop extending Mixin::Owned; - alter type Comments::Thread drop extending Mixin::Owned; - alter type Prompt::PromptVariantResponse drop extending Mixin::Owned; - alter type Prompt::VariantResponse drop extending Mixin::Owned; - alter type ProgressReport::Media drop extending Mixin::Owned; - alter type default::Post drop extending Mixin::Owned; - drop type Mixin::Owned; - - # Adjust globals - alter type Mixin::Pinnable { - drop property pinned; - }; - alter type default::Post { - drop property isMember; - }; - alter type Project::ContextAware { - drop property isMember; - }; - alter type default::Project { - drop link membership; - }; - alter type ProgressReport::WorkflowEvent { - alter link who { - reset default; - }; - }; - drop alias currentUser; - alter global currentUserId rename to currentActorId; - create global currentUser := (select User filter .id = global currentActorId); - create global currentRoles := (global currentActor).roles; - alter type ProgressReport::WorkflowEvent { - alter link who { - set default := global default::currentUser; - }; - }; - alter type default::Project { - create single link membership := (select .members filter .user = global default::currentUser limit 1); - }; - alter type Project::ContextAware { - create required single property isMember := exists .projectContext.projects.membership; - }; - alter type default::Post { - create single property isMember := .container[is Project::ContextAware].isMember; - }; - alter type Mixin::Pinnable { - create property pinned := ( - with user := (select default::User filter .id = global default::currentActorId) - select __source__ in user.pins - ); - }; - - alter type Mixin::Audited { - create required property isCreator := (.createdBy ?= global default::currentActor); - }; - - # Adjust triggers to assign defaults explicitly, because bug. - alter type default::InternshipEngagement { - alter trigger connectCertificationCeremony using ( - insert Engagement::CertificationCeremony { - createdAt := std::datetime_of_statement(), - modifiedAt := std::datetime_of_statement(), - createdBy := std::assert_exists(global default::currentActor), - modifiedBy := std::assert_exists(global default::currentActor), - engagement := __new__, - project := __new__.project, - projectContext := __new__.projectContext - } - ); - }; - alter type default::LanguageEngagement { - alter trigger connectDedicationCeremony using ( - insert Engagement::DedicationCeremony { - createdAt := std::datetime_of_statement(), - modifiedAt := std::datetime_of_statement(), - createdBy := std::assert_exists(global default::currentActor), - modifiedBy := std::assert_exists(global default::currentActor), - engagement := __new__, - project := __new__.project, - projectContext := __new__.projectContext - } - ); - }; - alter type default::Project { - alter trigger createBudgetOnInsert using ( - insert default::Budget { - createdAt := std::datetime_of_statement(), - modifiedAt := std::datetime_of_statement(), - createdBy := std::assert_exists(global default::currentActor), - modifiedBy := std::assert_exists(global default::currentActor), - project := __new__, - projectContext := __new__.projectContext - } - ); - }; - - # Regenerate policies - ALTER TYPE Budget::Record { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForBudgetRecord - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium))))); - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudgetRecord - ALLOW UPDATE WRITE, DELETE, INSERT ; - }; - ALTER TYPE Comments::Aware { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForCommentable - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCommentable - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForCommentable - ALLOW UPDATE WRITE ; - }; - ALTER TYPE Comments::Comment { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForComment - ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForComment - ALLOW INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForComment - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles)) OR .isCreator)); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForComment - ALLOW UPDATE WRITE ; - }; - ALTER TYPE Comments::Thread { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForCommentThread - ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForCommentThread - ALLOW INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCommentThread - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles)) OR .isCreator)); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForCommentThread - ALLOW UPDATE WRITE ; - }; - ALTER TYPE Engagement::Ceremony { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForCeremony - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForCeremony - ALLOW UPDATE WRITE, DELETE, INSERT ; - }; - ALTER TYPE Ethnologue::Language { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForEthnologueLanguage - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEthnologueLanguage - ALLOW SELECT, UPDATE READ USING (((((EXISTS (({'Administrator', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.sensitivity <= default::Sensitivity.Medium))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR ((default::Role.Fundraising IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Marketing', 'Fundraising', 'ExperienceOperations'} INTERSECT GLOBAL default::currentRoles)) AND (.sensitivity <= default::Sensitivity.Low)))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEthnologueLanguage - ALLOW UPDATE WRITE ; - }; - ALTER TYPE File::Node { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFileNode - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFileNode - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFileNode - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Directory { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForDirectory - ALLOW SELECT, UPDATE READ USING (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles))); - }; - ALTER TYPE default::Media { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForMedia - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForMedia - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForMedia - ALLOW UPDATE WRITE ; - }; - ALTER TYPE Mixin::Postable { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForPostable - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPostable - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPostable - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Project { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProject - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProject - ALLOW INSERT USING (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProject - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Fundraising', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'RegionalDirector', 'FieldOperationsDirector', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProject - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Language { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForLanguage - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForLanguage - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Fundraising', 'Marketing', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForLanguage - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Partner { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForPartner - ALLOW DELETE USING (EXISTS (({'Administrator', 'Controller'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForPartner - ALLOW INSERT USING (EXISTS (({'Administrator', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPartner - ALLOW SELECT, UPDATE READ USING (((((EXISTS (({'Administrator', 'ConsultantManager', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ExperienceOperations', 'Fundraising'} INTERSECT GLOBAL default::currentRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND ((.isMember AND (.sensitivity <= default::Sensitivity.Medium)) OR (.sensitivity <= default::Sensitivity.Low)))) OR ((default::Role.StaffMember IN GLOBAL default::currentRoles) AND (.sensitivity <= default::Sensitivity.Low)))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPartner - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::CommunityStory { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW INSERT USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportCommunityStory - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::Highlight { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW INSERT USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportHighlight - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::Media { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportMedia - ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportMedia - ALLOW INSERT USING ((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR (((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'draft'))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND (.variant = 'published'))) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.variant IN {'draft', 'translated', 'fpm'}))) OR (((default::Role.Translator IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'translated')))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportMedia - ALLOW SELECT, UPDATE READ USING (((((((EXISTS (({'Administrator', 'Leadership', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'draft'))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND (((.isMember AND (.variant IN {'draft', 'translated', 'fpm'})) OR ((.sensitivity <= default::Sensitivity.Low) AND (.variant IN {'fpm', 'published'}))) OR .isMember))) OR ((default::Role.Translator IN GLOBAL default::currentRoles) AND ((.isMember AND (.variant = 'translated')) OR .isMember))) OR .isCreator)); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportMedia - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::TeamNews { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW INSERT USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportTeamNews - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::VarianceExplanation { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForProgressReportVarianceExplanation - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportVarianceExplanation - ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportVarianceExplanation - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::WorkflowEvent { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW INSERT USING ((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND ((.transitionId IN {'5da76b5163', 'cb18f58cbf', '651d2a4dcc', 'e14c52346b'}) ?? false))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND ((.transitionId = '2d88e3cd6e') ?? false))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionId IN {'5da76b5163', 'cb18f58cbf', '651d2a4dcc', '580377ea2b', '0d854e832e', 'e14c52346b', '2b137bcd66', 'a0c0c48a8c', 'e3e11c86b9'}) ?? false))) OR ((default::Role.Translator IN GLOBAL default::currentRoles) AND ((.transitionId IN {'580377ea2b', '0d854e832e'}) ?? false)))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership', 'Marketing', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProgressReportWorkflowEvent - ALLOW UPDATE WRITE ; - }; - ALTER TYPE ProgressReport::ProductProgress::Step { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForStepProgress - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForStepProgress - ALLOW SELECT, UPDATE READ USING (((((EXISTS (({'Administrator', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (((default::Role.FieldPartner IN GLOBAL default::currentRoles) AND .isMember) AND (.variant = 'partner'))) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.variant IN {'official', 'partner'}))) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForStepProgress - ALLOW UPDATE WRITE ; - }; - ALTER TYPE Project::Member { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForProjectMember - ALLOW DELETE, INSERT USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProjectMember - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProjectMember - ALLOW UPDATE WRITE ; - }; - ALTER TYPE User::Education { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForEducation - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForEducation - ALLOW INSERT USING (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEducation - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'ConsultantManager', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEducation - ALLOW UPDATE WRITE ; - }; - ALTER TYPE User::Unavailability { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForUnavailability - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForUnavailability - ALLOW INSERT USING (EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForUnavailability - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT GLOBAL default::currentRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'})))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForUnavailability - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Budget { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForBudget - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles)) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium))))); - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForBudget - ALLOW UPDATE WRITE, DELETE, INSERT ; - }; - ALTER TYPE default::Engagement { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForEngagement - ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.status = 'InDevelopment')))); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForEngagement - ALLOW INSERT USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND (.project.status = 'InDevelopment')))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForEngagement - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner', 'Intern', 'Mentor', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForEngagement - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::LanguageEngagement { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForLanguageEngagement - ALLOW SELECT, UPDATE READ USING ((default::Role.ConsultantManager IN GLOBAL default::currentRoles)); - }; - ALTER TYPE default::Producible { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProducible - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProducible - ALLOW INSERT USING (EXISTS (({'Administrator', 'FieldOperationsDirector', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForProducible - ALLOW SELECT, UPDATE ; - }; - ALTER TYPE default::FieldRegion { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFieldRegion - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFieldRegion - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFieldRegion - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::FieldZone { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFieldZone - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFieldZone - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFieldZone - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::PeriodicReport { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPeriodicReport - ALLOW SELECT, UPDATE READ USING (WITH - isMember := - (.container[IS Project::ContextAware].isMember ?? false) - , - sensitivity := - (.container[IS Project::ContextAware].sensitivity ?? default::Sensitivity.High) - SELECT - ((EXISTS (({'Administrator', 'ExperienceOperations', 'FieldOperationsDirector', 'FieldPartner', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'ConsultantManager', 'Marketing', 'Fundraising', 'ExperienceOperations'} INTERSECT GLOBAL default::currentRoles)) AND (isMember OR (sensitivity <= default::Sensitivity.Medium)))) OR (EXISTS (({'Consultant', 'ConsultantManager', 'Intern', 'Mentor', 'Translator'} INTERSECT GLOBAL default::currentRoles)) AND isMember)) - ); - CREATE ACCESS POLICY CanUpdateWriteInsertDeleteGeneratedFromAppPoliciesForPeriodicReport - ALLOW UPDATE WRITE, DELETE, INSERT ; - }; - ALTER TYPE default::FundingAccount { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFundingAccount - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFundingAccount - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'ConsultantManager', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFundingAccount - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Location { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForLocation - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadUpdateWriteGeneratedFromAppPoliciesForLocation - ALLOW SELECT, UPDATE ; - }; - ALTER TYPE default::NarrativeReport { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForNarrativeReport - ALLOW SELECT, UPDATE READ USING (WITH - isMember := - (.container[IS Project::ContextAware].isMember ?? false) - SELECT - (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND isMember) - ); - }; - ALTER TYPE default::Organization { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForOrganization - ALLOW DELETE USING (EXISTS (({'Administrator', 'Controller'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForOrganization - ALLOW INSERT USING (EXISTS (({'Administrator', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForOrganization - ALLOW SELECT, UPDATE READ USING ((((EXISTS (({'Administrator', 'ConsultantManager', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ExperienceOperations', 'Fundraising'} INTERSECT GLOBAL default::currentRoles)) AND (.sensitivity <= default::Sensitivity.Medium))) OR ((default::Role.Marketing IN GLOBAL default::currentRoles) AND (.isMember OR (.sensitivity <= default::Sensitivity.Low))))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForOrganization - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::Partnership { - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForPartnership - ALLOW DELETE, INSERT USING (((EXISTS (({'Administrator', 'FieldOperationsDirector', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium))))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPartnership - ALLOW SELECT, UPDATE READ USING ((((EXISTS (({'Administrator', 'ConsultantManager', 'ExperienceOperations', 'LeadFinancialAnalyst', 'Controller', 'FinancialAnalyst', 'Marketing', 'Fundraising', 'Leadership', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'Consultant', 'ConsultantManager', 'FieldPartner'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND (.isMember OR (.sensitivity <= default::Sensitivity.Medium)))) OR ((default::Role.StaffMember IN GLOBAL default::currentRoles) AND (.sensitivity <= default::Sensitivity.Low)))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPartnership - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::ProgressReport { - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProgressReport - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember)); - }; - ALTER TYPE default::Post { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForPost - ALLOW DELETE USING (((default::Role.Administrator IN GLOBAL default::currentRoles) OR .isCreator)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForPost - ALLOW INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForPost - ALLOW SELECT, UPDATE READ USING ((EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles)) OR .isCreator)); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForPost - ALLOW UPDATE WRITE ; - }; - ALTER TYPE default::User { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForUser - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForUser - ALLOW INSERT USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForUser - ALLOW SELECT, UPDATE READ USING (((EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'FieldPartner', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller', 'Marketing', 'Fundraising', 'ExperienceOperations', 'Leadership', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector', 'StaffMember'} INTERSECT GLOBAL default::currentRoles)) OR (.id ?= GLOBAL default::currentActorId)) OR (EXISTS (({'Intern', 'Mentor'} INTERSECT GLOBAL default::currentRoles)) AND EXISTS ({'Stubbed .isMember for User/Unavailability'})))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForUser - ALLOW UPDATE WRITE ; - }; -}; diff --git a/dbschema/migrations/00007-m15wwix.edgeql b/dbschema/migrations/00007-m15wwix.edgeql deleted file mode 100644 index e4b423ec30..0000000000 --- a/dbschema/migrations/00007-m15wwix.edgeql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE MIGRATION m1s2cbqfqayiw2giggpp3dlfwrxnmpaziw7irc4h74chugwr4noluq - ONTO m1rxhi72zd43b4hwanwavpsfx7yo5vfngfr4ngmxq4thpuvi3t45qa -{ - ALTER TYPE default::Organization { - CREATE PROPERTY address: std::str; - }; - ALTER TYPE default::Partner { - CREATE PROPERTY address: std::str; - CREATE PROPERTY startDate: cal::local_date; - }; -}; diff --git a/dbschema/migrations/00008-m1pgopp.edgeql b/dbschema/migrations/00008-m1pgopp.edgeql deleted file mode 100644 index fe24e0c897..0000000000 --- a/dbschema/migrations/00008-m1pgopp.edgeql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE MIGRATION m1gq2hsptfudyqzcqhaz3o5ikdckynzcdegdixqtrdtnisldpyqv6a - ONTO m1s2cbqfqayiw2giggpp3dlfwrxnmpaziw7irc4h74chugwr4noluq -{ - CREATE SCALAR TYPE Project::Type EXTENDING enum; - CREATE TYPE Project::FinancialApprover { - CREATE REQUIRED LINK user: default::User { - CREATE CONSTRAINT std::exclusive; - }; - CREATE REQUIRED MULTI PROPERTY projectTypes: Project::Type; - CREATE ACCESS POLICY CanInsertDeleteGeneratedFromAppPoliciesForFinancialApprover - ALLOW DELETE, INSERT USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForFinancialApprover - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Leadership'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForFinancialApprover - ALLOW UPDATE WRITE; - }; -}; diff --git a/dbschema/migrations/00009-m1bzq66.edgeql b/dbschema/migrations/00009-m1bzq66.edgeql deleted file mode 100644 index ebf76e74ae..0000000000 --- a/dbschema/migrations/00009-m1bzq66.edgeql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE MIGRATION m1bzq66kx6aa3adornnuhmqpmmqmj6z6i2qt6higxnkz7kw3fspcbq - ONTO m1gq2hsptfudyqzcqhaz3o5ikdckynzcdegdixqtrdtnisldpyqv6a -{ - ALTER TYPE default::InternshipEngagement { - CREATE MULTI PROPERTY methodologies: Product::Methodology; - }; - CREATE SCALAR TYPE Engagement::InternPosition EXTENDING enum; - ALTER TYPE default::InternshipEngagement { - CREATE PROPERTY position: Engagement::InternPosition; - }; -}; diff --git a/dbschema/migrations/00010-m1sznh4.edgeql b/dbschema/migrations/00010-m1sznh4.edgeql deleted file mode 100644 index 5287165f59..0000000000 --- a/dbschema/migrations/00010-m1sznh4.edgeql +++ /dev/null @@ -1,70 +0,0 @@ -CREATE MIGRATION m1sznh4tznllmlcsn2n3wxrwih5syqmxdc3fjtovmy4grty2qhczka - ONTO m1bzq66kx6aa3adornnuhmqpmmqmj6z6i2qt6higxnkz7kw3fspcbq -{ - CREATE TYPE Project::WorkflowEvent { - CREATE ACCESS POLICY CanDeleteGeneratedFromAppPoliciesForProjectWorkflowEvent - ALLOW DELETE USING ((default::Role.Administrator IN GLOBAL default::currentRoles)); - CREATE PROPERTY transitionKey: std::uuid { - SET readonly := true; - }; - CREATE ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProjectWorkflowEvent - ALLOW INSERT USING ((((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR (EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'b4feb4f5-1687-5012-9111-ec4c4c950eff', 'ac603cdb-e6a8-51de-b29f-866f4cd8df6e'}) ?? false))) OR ((default::Role.Controller IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'a2a8faef-daea-5221-9c8e-a9b4d3447958', '9499026e-9ed0-5e56-a8fd-9f976b6253a2', 'd114e21f-f697-5eed-bbce-f6b3466e5314', 'dadc642c-4013-522a-b341-1cf7db1a589f', '301de915-b934-5b5f-a4cb-889fff26fe22', '0429940c-e1e0-51e5-8cdc-a17ad2488ce4', 'c7cc144b-2625-5ad8-bca9-a95729c807b9', '5b725348-1283-5025-8146-0ed53b3efeae', 'ab056cf0-05af-57ca-8510-b0e1dfc38726'}) ?? false))) OR ((default::Role.FieldOperationsDirector IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'f3e4492c-1e9e-5d49-960b-f6aa21ba3062', '47ed9fbc-7c6d-50df-a731-e41f8a925c6c', 'e5774cd9-ddba-57d0-9766-c9599b7c360b'}) ?? false))) OR (EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'f506e3e8-f4af-5348-9c6c-08e79f8d54fa', '094176ef-407c-5599-aa04-d4181b6a175b', '963270ba-4fb7-5fcc-986b-9c169aaa09fc', 'ac2918a6-63d4-5c65-99ab-1609349cef54'}) ?? false))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'978c2e89-90de-5151-ab04-96e02bd6cb86', 'ba838c42-a3ec-523c-9579-253d7c40c8d5', '31c540f2-81af-54fc-bdd1-72863a244b87', '67250264-3cd6-5eb3-ade0-ce35d1535bf0', '92351eeb-3045-5790-80eb-cc60f1832224', 'f1ec20d9-607a-5f72-a0b0-265ef2ca38f0', 'b4feb4f5-1687-5012-9111-ec4c4c950eff', 'ac603cdb-e6a8-51de-b29f-866f4cd8df6e', '1e804776-a92b-5d9c-96b5-80494546547c', '7b53971b-eab7-5f77-a483-a1970161594c', '5ee57376-eb07-547e-97a3-d5e8d3d5e336', 'cf56600f-e5f7-5c97-8b35-ed69c7c2685c', '772129cf-960e-58e9-a37a-6d215b16b91d', '8f24f37c-2fa9-5eac-b275-e5e6048eaf3c', 'd44dab7e-0634-5c3b-96a6-0428e723977c', 'e24cd4d8-bb8b-5373-a872-36b2122b2ec3', '59d412d0-1c44-598d-8387-d02e7e82e816', '0a910217-643a-5d2a-a1c0-1e391fbf7bfd', '4e06fe25-d6f7-5d35-8e5e-e4f0f4fe56d8', 'fed769bf-1124-5798-9274-19214a08c1b9', '0e5cc01e-977c-516b-9116-a3705e4aff29', 'ee88de05-88cb-5db2-94d2-c81f736419c6', '00588873-c813-56a7-b44d-524f0fa4ee9c', '05bd4f8d-e763-508a-b44e-aec4ea6ada53', '96b07f99-121d-5b9b-a5ae-abc897c4c8dd', 'dee9f990-4b17-53f8-abcb-61cab8033895', '808fc73d-a8b9-5374-9087-7dacf20ea66e', '783440f6-0836-54ec-aacd-4082d8ac3a1b', '963270ba-4fb7-5fcc-986b-9c169aaa09fc', 'ac2918a6-63d4-5c65-99ab-1609349cef54'}) ?? false))) OR (EXISTS (({'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'03577b19-d799-5029-af29-d02c3f8926a0', '9ea3a784-7d79-57e3-9d6b-b84e45283631', '12af5736-92bb-5097-868d-8a4fe3691a47', '7a0cbaf1-7113-523f-8339-15781b9435a5', '702b47e7-ad81-5843-8e33-3a6d24d9413f', 'd59f946b-d7b4-5e09-90ac-9b3adff7a15c', 'cf79e84e-1485-533a-9eca-48d1dd89f6ae', '0d4433e8-4fb3-5119-a023-f248b0428220', '21231706-d17f-5273-961a-bd90db115f6d', '2c3a86f7-bf90-5ca2-a022-d9382161601f', 'd149f82b-b2fe-5f4b-8f5d-9f24f48a7006', '7a980e5d-58fe-5f0f-aca5-d8dad2483f02', '38a941c4-1c50-504f-a98e-e3a6edcc5459', '934c2439-98b2-568e-b943-4181c4e94b7c', '732b175a-f41d-59a3-a4ef-9f47a72355a0', '591ec845-e612-5e82-9061-5176d24e3ab1', '28da9161-dbf2-5ada-8b92-42a230fe35c8', '17214902-01da-5b7f-8575-252ce3f9fdd7', 'd01a7fa7-c7bf-5e08-a610-3797febe9f97'}) ?? false)))); - CREATE ACCESS POLICY CanSelectUpdateReadGeneratedFromAppPoliciesForProjectWorkflowEvent - ALLOW SELECT, UPDATE READ USING (EXISTS (({'Administrator', 'Consultant', 'ConsultantManager', 'Controller', 'FieldOperationsDirector', 'FinancialAnalyst', 'LeadFinancialAnalyst', 'Leadership', 'ProjectManager', 'RegionalDirector'} INTERSECT GLOBAL default::currentRoles))); - CREATE ACCESS POLICY CanUpdateWriteGeneratedFromAppPoliciesForProjectWorkflowEvent - ALLOW UPDATE WRITE ; - CREATE REQUIRED LINK project: default::Project { - ON TARGET DELETE DELETE SOURCE; - SET readonly := true; - }; - CREATE REQUIRED PROPERTY at: std::datetime { - SET default := (std::datetime_of_statement()); - SET readonly := true; - }; - CREATE REQUIRED PROPERTY to: Project::Step { - SET readonly := true; - }; - CREATE REQUIRED LINK who: default::Actor { - SET default := (GLOBAL default::currentActor); - SET readonly := true; - }; - CREATE PROPERTY notes: default::RichText { - SET readonly := true; - }; - }; - ALTER TYPE default::Project { - CREATE LINK workflowEvents := (.(.__type__.name)[9:-7]); - }; - ALTER TYPE Project::WorkflowEvent { - ALTER ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProjectWorkflowEvent USING ((((((((((default::Role.Administrator IN GLOBAL default::currentRoles) OR ((default::Role.ConsultantManager IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false))) OR ((EXISTS (({'Consultant', 'ConsultantManager'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND ((.transitionKey IN {'1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false))) OR ((default::Role.Controller IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'3c9662f5-db67-5403-b416-7aeeef0fb350', 'a050d4e1-b446-52ab-b6c9-1ab045aa91f5', 'ffff1e49-94ca-5601-ada5-6cc52c93d517', '78af1187-552f-5f7a-b6ec-c737d300aaa9', 'b65b9db4-d5b3-5557-9c1e-a25fc9edc538', '0d9f9039-7099-5faf-9b08-099d47a5cc42', 'a1088f2c-478c-5512-8a60-e4feff5538cc', 'c4663d2d-8b6d-5f45-8fb3-cb71e34555a0', 'ce663888-ca28-55cd-9e9b-1ea5b75e76b2'}) ?? false))) OR ((default::Role.FieldOperationsDirector IN GLOBAL default::currentRoles) AND ((.transitionKey IN {'a03d96d7-bc75-51d4-b793-88aa02e26cfc', '2f012ffe-893f-52ea-b9f0-39f313b0dd1f', 'aa718b6c-8f16-589c-a0d4-50d5555534d1'}) ?? false))) OR (EXISTS (({'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'f48d59a0-67e7-57e2-9f7c-8f9cd8c3c01c', '857c7594-8af8-524e-a61f-15b6ac2bac7d', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa'}) ?? false))) OR ((EXISTS (({'FinancialAnalyst', 'LeadFinancialAnalyst', 'Controller'} INTERSECT GLOBAL default::currentRoles)) AND .isMember) AND ((.transitionKey IN {'f48d59a0-67e7-57e2-9f7c-8f9cd8c3c01c', '857c7594-8af8-524e-a61f-15b6ac2bac7d', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa'}) ?? false))) OR (EXISTS (({'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((((.project.type = 'MomentumTranslation') AND .isMember) AND ((.transitionKey IN {'1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false)) OR (.isMember AND ((.transitionKey IN {'6ce4c5d0-5034-55c1-bf80-bf5992fe38bf', '37e42c6d-260b-51c7-98c3-d918ba057480', '4edf8b35-b462-590d-bc2c-fd0d20b46a6d', '06c4afc9-2349-50ae-ae4b-a506f5b80dee', '9d2c13e3-d73f-58bc-b68d-c447f0caa91d', 'afe0b316-57ef-5640-a098-4fa1cf0a81b9', '356c7be5-08f5-5f36-a8ad-dfc5509a872e', '3c5607fb-1bba-5c02-a7de-24e4bcf2f27b', 'aa17add1-15eb-5054-80be-809480f8b0eb', '4d495617-2fa4-5dc3-a5ec-63ae21731f1c', '2dc45f2c-c7c8-5f60-9f8c-b9994fcd6b82', '45100e48-16f0-5ac4-a0cb-7e8214414bf8', '04556e06-0efe-5bad-b7a7-9b2524e9c9cf', 'c85a70bb-ff30-5540-8c75-f0c543609418', '479ce997-0cb6-5c38-af9b-a36d3ea24e82', '792e3e28-8237-53a3-893b-e6d4e5476265', '60483d90-c54f-5dd4-b233-b53287ba4324', '7f9b7302-3ea4-5b77-b9ac-7191d11adb94', '006aebaa-83df-5225-add8-062c498191fe', '77ba67dd-b2c3-5e7b-9f33-f55c09279f3e', '0c610243-ba8f-565c-ab17-1b62e1fa6d06', '2537d694-d712-5d3f-8c8a-b07002265e39', '2267fe55-7a97-5aa1-bbc1-403e59f71f9d', 'fe0ec249-f6d6-5ce7-97c4-c19720a057cd', '758a333a-7484-5fb4-aa4c-9c620f542551', '2063137a-7901-5185-b1f8-fe76c56f5c67', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa'}) ?? false))))) OR (EXISTS (({'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND ((.transitionKey IN {'3e687176-4f02-5f2e-ad5a-374f39504034', '60e21d6d-d4e0-5103-8eee-4f4ff80404b1', '2126603a-13bc-5356-a062-d11647c0f5f6', 'd22e15d9-c4cb-564d-b429-8cef0d482e14', 'b412a3de-d8b9-5068-9e90-e6ead0b4407c', '61b12ecf-6024-54eb-9f49-83b1dc6769f1', '9c8e953f-eecd-5a42-a806-e0e3187dda66', '99dcddcb-4351-5643-a196-9fd93fe68760', 'd5ead4fc-5d99-5a82-8c32-8640ffe26162', 'aa4a45d0-d611-56f2-bd90-d47fc4626d60', 'd9aaa5d1-da74-5042-915f-e7726367ea4e', 'bcfc9f9b-c6d6-59c3-8449-42620f806211', 'f1694429-c062-5895-bff1-12e73cf8eea5', '90fb9642-7b38-510c-bc45-84e722c145b6', 'dae01c7f-13d4-5246-a3f6-2fa3e067b505', 'f7511418-bce7-5901-b141-06a470955a86', '3e19a59f-a414-5c58-a1ff-7bfc6cff7eef', '6b08ebc1-5279-58fc-b2c5-26147b278426', '05d1a69d-e717-5ca9-b011-688cf58a924f', '6ce4c5d0-5034-55c1-bf80-bf5992fe38bf', '37e42c6d-260b-51c7-98c3-d918ba057480', '4edf8b35-b462-590d-bc2c-fd0d20b46a6d', '06c4afc9-2349-50ae-ae4b-a506f5b80dee', '9d2c13e3-d73f-58bc-b68d-c447f0caa91d', 'afe0b316-57ef-5640-a098-4fa1cf0a81b9', '356c7be5-08f5-5f36-a8ad-dfc5509a872e', '3c5607fb-1bba-5c02-a7de-24e4bcf2f27b', 'aa17add1-15eb-5054-80be-809480f8b0eb', '4d495617-2fa4-5dc3-a5ec-63ae21731f1c', '2dc45f2c-c7c8-5f60-9f8c-b9994fcd6b82', '45100e48-16f0-5ac4-a0cb-7e8214414bf8', '04556e06-0efe-5bad-b7a7-9b2524e9c9cf', 'c85a70bb-ff30-5540-8c75-f0c543609418', '479ce997-0cb6-5c38-af9b-a36d3ea24e82', '792e3e28-8237-53a3-893b-e6d4e5476265', '60483d90-c54f-5dd4-b233-b53287ba4324', '7f9b7302-3ea4-5b77-b9ac-7191d11adb94', '006aebaa-83df-5225-add8-062c498191fe', '77ba67dd-b2c3-5e7b-9f33-f55c09279f3e', '0c610243-ba8f-565c-ab17-1b62e1fa6d06', '2537d694-d712-5d3f-8c8a-b07002265e39', '2267fe55-7a97-5aa1-bbc1-403e59f71f9d', 'fe0ec249-f6d6-5ce7-97c4-c19720a057cd', '758a333a-7484-5fb4-aa4c-9c620f542551', '2063137a-7901-5185-b1f8-fe76c56f5c67', 'f4726d2a-f92d-58e5-a360-a22560f65971', '9cac7636-b5af-57c7-977d-ddaa2cd837aa', '1397f444-2dad-5467-aa96-107eae27d807', '7f23ab0e-fe6c-58a5-aaa1-7dd209e3e79a'}) ?? false)))); - }; -}; diff --git a/dbschema/migrations/00012-m1ah2np.edgeql b/dbschema/migrations/00012-m1ah2np.edgeql deleted file mode 100644 index eb2686fbcd..0000000000 --- a/dbschema/migrations/00012-m1ah2np.edgeql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE MIGRATION m1ah2npzra5pek4sq777ow5uvmwuxeediqm5kp2x2zek2zlyjdwg5q - ONTO m17u4aufxga7wgmcebhiaapulc7xrqg34hcbmucddbvm4tw5c63kyq -{ - ALTER TYPE default::Language { - ALTER PROPERTY registryOfDialectsCode { - RENAME TO registryOfLanguageVarietiesCode; - }; - }; -}; diff --git a/dbschema/migrations/00013-m1wi5kt.edgeql b/dbschema/migrations/00013-m1wi5kt.edgeql deleted file mode 100644 index dec10a8152..0000000000 --- a/dbschema/migrations/00013-m1wi5kt.edgeql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE MIGRATION m1wi5ktrz2nhc2nd4nqdedbhronuklnbkz3dk7jexegedolhtrruoq - ONTO m1ah2npzra5pek4sq777ow5uvmwuxeediqm5kp2x2zek2zlyjdwg5q -{ - ALTER TYPE ProgressReport::CommunityStory { - ALTER ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportCommunityStory USING ((EXISTS (({'Administrator', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - }; - ALTER TYPE ProgressReport::Highlight { - ALTER ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportHighlight USING ((EXISTS (({'Administrator', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - }; - ALTER TYPE ProgressReport::TeamNews { - ALTER ACCESS POLICY CanInsertGeneratedFromAppPoliciesForProgressReportTeamNews USING ((EXISTS (({'Administrator', 'Marketing'} INTERSECT GLOBAL default::currentRoles)) OR (EXISTS (({'FieldPartner', 'ProjectManager', 'RegionalDirector', 'FieldOperationsDirector'} INTERSECT GLOBAL default::currentRoles)) AND .isMember))); - }; -}; From cd127df16bb03c2b5ed04a25a7f70df31c5f5983 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 11:04:52 -0500 Subject: [PATCH 02/56] Skip initial end date & delete engagement handlers for EdgeDB --- .../delete-engagement-default-ceremony.handler.ts | 11 +++++++++-- .../handlers/set-initial-end-date.handler.ts | 12 +++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts b/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts index 89aed2607f..f146fa849d 100644 --- a/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts +++ b/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts @@ -1,4 +1,4 @@ -import { EventsHandler, IEventHandler } from '~/core'; +import { ConfigService, EventsHandler, IEventHandler } from '~/core'; import { EngagementWillDeleteEvent } from '../../engagement/events'; import { CeremonyService } from '../ceremony.service'; @@ -6,9 +6,16 @@ import { CeremonyService } from '../ceremony.service'; export class DetachEngagementRootDirectoryHandler implements IEventHandler { - constructor(private readonly ceremonies: CeremonyService) {} + constructor( + private readonly ceremonies: CeremonyService, + private readonly config: ConfigService, + ) {} async handle({ engagement, session }: EngagementWillDeleteEvent) { + if (this.config.databaseEngine === 'edgedb') { + return; + } + const ceremonyId = engagement?.ceremony?.value; if (!ceremonyId) { return; diff --git a/src/components/engagement/handlers/set-initial-end-date.handler.ts b/src/components/engagement/handlers/set-initial-end-date.handler.ts index cdb3accb56..5ce5395c04 100644 --- a/src/components/engagement/handlers/set-initial-end-date.handler.ts +++ b/src/components/engagement/handlers/set-initial-end-date.handler.ts @@ -1,5 +1,11 @@ import { CalendarDate, ID, ServerException, UnsecuredDto } from '~/common'; -import { EventsHandler, IEventHandler, ILogger, Logger } from '~/core'; +import { + ConfigService, + EventsHandler, + IEventHandler, + ILogger, + Logger, +} from '~/core'; import { Engagement, EngagementStatus, @@ -17,10 +23,14 @@ export class SetInitialEndDate implements IEventHandler { constructor( private readonly engagementRepo: EngagementRepository, private readonly engagementService: EngagementService, + private readonly config: ConfigService, @Logger('engagement:set-initial-end-date') private readonly logger: ILogger, ) {} async handle(event: SubscribedEvent) { + if (this.config.databaseEngine === 'edgedb') { + return; + } this.logger.debug('Engagement mutation, set initial end date', { ...event, event: event.constructor.name, From 7652fa1d853ee94f8150de935e3d1c86e743b898 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 11:50:54 -0500 Subject: [PATCH 03/56] Refactor Engagement relationship IDs to Links Co-authored-by: Bryan Nelson --- ...create-engagement-default-ceremony.handler.ts | 2 +- ...delete-engagement-default-ceremony.handler.ts | 2 +- src/components/engagement/dto/engagement.dto.ts | 16 +++++++--------- .../engagement/engagement.repository.ts | 11 ++++++----- src/components/engagement/engagement.resolver.ts | 2 +- .../engagement/internship-engagement.resolver.ts | 6 +++--- .../engagement/language-engagement.resolver.ts | 2 +- .../extract-products-from-pnp.handler.ts | 2 +- 8 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts b/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts index d699d1f539..86f38788bd 100644 --- a/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts +++ b/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts @@ -48,7 +48,7 @@ export class CreateEngagementDefaultCeremonyHandler event.engagement = { ...engagement, - ceremony: ceremonyId, + ceremony: { id: ceremonyId }, }; } } diff --git a/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts b/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts index f146fa849d..64a92ddfbf 100644 --- a/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts +++ b/src/components/ceremony/handlers/delete-engagement-default-ceremony.handler.ts @@ -16,7 +16,7 @@ export class DetachEngagementRootDirectoryHandler return; } - const ceremonyId = engagement?.ceremony?.value; + const ceremonyId = engagement?.ceremony?.value?.id; if (!ceremonyId) { return; } diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index 00ca49bdf5..893d3eb055 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -7,7 +7,6 @@ import { DateInterval, DateTimeField, DbLabel, - ID, IntersectTypes, parentIdMiddleware, Resource, @@ -28,7 +27,6 @@ import { e } from '~/core/edgedb'; import { LinkTo, RegisterResource } from '~/core/resources'; import { ScopedRole } from '../../authorization/dto'; import { ChangesetAware } from '../../changeset/dto'; -import { DefinedFile } from '../../file/dto'; import { Product, SecuredMethodologies } from '../../product/dto'; import { InternshipProject, @@ -80,7 +78,7 @@ class Engagement extends Interfaces { @DbLabel('EngagementStatus') readonly status: SecuredEngagementStatus; - readonly ceremony: Secured; + readonly ceremony: Secured>; @Field({ description: 'Translation / Growth Plan complete date', @@ -166,7 +164,7 @@ export class LanguageEngagement extends Engagement { @Field(() => TranslationProject) declare readonly parent: BaseNode; - readonly language: Secured; + readonly language: Secured>; @Field() readonly firstScripture: SecuredBoolean; @@ -185,7 +183,7 @@ export class LanguageEngagement extends Engagement { @Field() readonly paratextRegistryId: SecuredString; - readonly pnp: DefinedFile; + readonly pnp: Secured>; @Field() readonly historicGoal: SecuredString; @@ -207,11 +205,11 @@ export class InternshipEngagement extends Engagement { @Field(() => InternshipProject) declare readonly parent: BaseNode; - readonly countryOfOrigin: Secured; + readonly countryOfOrigin: Secured | null>; - readonly intern: Secured; + readonly intern: Secured>; - readonly mentor: Secured; + readonly mentor: Secured | null>; @Field() @DbLabel('InternPosition') @@ -221,7 +219,7 @@ export class InternshipEngagement extends Engagement { @DbLabel('ProductMethodology') readonly methodologies: SecuredMethodologies; - readonly growthPlan: DefinedFile; + readonly growthPlan: Secured>; } export const engagementRange = (engagement: UnsecuredDto) => diff --git a/src/components/engagement/engagement.repository.ts b/src/components/engagement/engagement.repository.ts index cb0c610300..9bafce92cf 100644 --- a/src/components/engagement/engagement.repository.ts +++ b/src/components/engagement/engagement.repository.ts @@ -171,11 +171,12 @@ export class EngagementRepository extends CommonRepository { type: 'project.type', status: 'status.value', }, - language: 'language.id', - ceremony: 'ceremony.id', - intern: 'intern.id', - countryOfOrigin: 'countryOfOrigin.id', - mentor: 'mentor.id', + language: 'language { .id }', + pnp: { id: 'props.pnp' }, + ceremony: 'ceremony { .id }', + intern: 'intern { .id }', + countryOfOrigin: 'countryOfOrigin { .id }', + mentor: 'mentor { .id }', startDate: coalesce( 'changedProps.startDateOverride', 'props.startDateOverride', diff --git a/src/components/engagement/engagement.resolver.ts b/src/components/engagement/engagement.resolver.ts index cb771c98ea..1bd0dbf445 100644 --- a/src/components/engagement/engagement.resolver.ts +++ b/src/components/engagement/engagement.resolver.ts @@ -104,7 +104,7 @@ export class EngagementResolver { @Parent() engagement: Engagement, @Loader(CeremonyLoader) ceremonies: LoaderOf, ): Promise { - return await mapSecuredValue(engagement.ceremony, (id) => + return await mapSecuredValue(engagement.ceremony, ({ id }) => ceremonies.load(id), ); } diff --git a/src/components/engagement/internship-engagement.resolver.ts b/src/components/engagement/internship-engagement.resolver.ts index 87d2089284..96ec81b80d 100644 --- a/src/components/engagement/internship-engagement.resolver.ts +++ b/src/components/engagement/internship-engagement.resolver.ts @@ -24,7 +24,7 @@ export class InternshipEngagementResolver { @Parent() engagement: InternshipEngagement, @Loader(UserLoader) users: LoaderOf, ): Promise { - return await mapSecuredValue(engagement.intern, (id) => users.load(id)); + return await mapSecuredValue(engagement.intern, ({ id }) => users.load(id)); } @ResolveField(() => SecuredUser) @@ -32,7 +32,7 @@ export class InternshipEngagementResolver { @Parent() engagement: InternshipEngagement, @Loader(UserLoader) users: LoaderOf, ): Promise { - return await mapSecuredValue(engagement.mentor, (id) => users.load(id)); + return await mapSecuredValue(engagement.mentor, ({ id }) => users.load(id)); } @ResolveField(() => SecuredLocation) @@ -40,7 +40,7 @@ export class InternshipEngagementResolver { @Parent() engagement: InternshipEngagement, @Loader(LocationLoader) locations: LoaderOf, ): Promise { - return await mapSecuredValue(engagement.countryOfOrigin, (id) => + return await mapSecuredValue(engagement.countryOfOrigin, ({ id }) => locations.load(id), ); } diff --git a/src/components/engagement/language-engagement.resolver.ts b/src/components/engagement/language-engagement.resolver.ts index fd469e4c69..d8df6d2d20 100644 --- a/src/components/engagement/language-engagement.resolver.ts +++ b/src/components/engagement/language-engagement.resolver.ts @@ -25,7 +25,7 @@ export class LanguageEngagementResolver { @Parent() engagement: LanguageEngagement, @Loader(LanguageLoader) languages: LoaderOf, ): Promise { - return await mapSecuredValue(engagement.language, (id) => + return await mapSecuredValue(engagement.language, ({ id }) => languages.load({ id, view: viewOfChangeset(engagement.changeset) }), ); } diff --git a/src/components/product/handlers/extract-products-from-pnp.handler.ts b/src/components/product/handlers/extract-products-from-pnp.handler.ts index b3af5f3922..48d8ec9824 100644 --- a/src/components/product/handlers/extract-products-from-pnp.handler.ts +++ b/src/components/product/handlers/extract-products-from-pnp.handler.ts @@ -33,7 +33,7 @@ export class ExtractProductsFromPnpHandler } const availableSteps = getAvailableSteps({ methodology }); - const file = await this.files.getFile(engagement.pnp, event.session); + const file = await this.files.getFile(engagement.pnp.id, event.session); const fv = await this.files.getFileVersion( file.latestVersionId, event.session, From e57b10d8abb224437cc14dd04a793aa6a5b3f762 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 11:53:02 -0500 Subject: [PATCH 04/56] Mark engagement files as nullable Co-authored-by: Bryan Nelson --- src/components/engagement/dto/engagement.dto.ts | 4 ++-- src/components/file/file.service.ts | 2 +- .../product/handlers/extract-products-from-pnp.handler.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index 893d3eb055..6eb342b90a 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -183,7 +183,7 @@ export class LanguageEngagement extends Engagement { @Field() readonly paratextRegistryId: SecuredString; - readonly pnp: Secured>; + readonly pnp: Secured | null>; @Field() readonly historicGoal: SecuredString; @@ -219,7 +219,7 @@ export class InternshipEngagement extends Engagement { @DbLabel('ProductMethodology') readonly methodologies: SecuredMethodologies; - readonly growthPlan: Secured>; + readonly growthPlan: Secured | null>; } export const engagementRange = (engagement: UnsecuredDto) => diff --git a/src/components/file/file.service.ts b/src/components/file/file.service.ts index 6fd4d64aa2..d65e425668 100644 --- a/src/components/file/file.service.ts +++ b/src/components/file/file.service.ts @@ -524,7 +524,7 @@ export class FileService { async updateDefinedFile< Input extends CreateDefinedFileVersionInput | undefined, >( - file: Secured>, + file: Secured | null>, field: string, input: Input, session: Session, diff --git a/src/components/product/handlers/extract-products-from-pnp.handler.ts b/src/components/product/handlers/extract-products-from-pnp.handler.ts index 48d8ec9824..38dcf49b6c 100644 --- a/src/components/product/handlers/extract-products-from-pnp.handler.ts +++ b/src/components/product/handlers/extract-products-from-pnp.handler.ts @@ -28,7 +28,7 @@ export class ExtractProductsFromPnpHandler ? event.engagement : event.updated; const { pnp: hasPnpInput, methodology } = event.input; - if (!hasPnpInput || !methodology) { + if (!engagement.pnp || !hasPnpInput || !methodology) { return; } const availableSteps = getAvailableSteps({ methodology }); From 17f0f592c1d7103bdbbd472f554b63eaf82d827d Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 11:53:56 -0500 Subject: [PATCH 05/56] Fix engagement fields to be nullable types where applicable Co-authored-by: Bryan Nelson --- src/components/engagement/dto/engagement.dto.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index 6eb342b90a..d7b10094d3 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -14,10 +14,10 @@ import { Secured, SecuredBoolean, SecuredDateNullable, - SecuredDateTime, + SecuredDateTimeNullable, SecuredProps, SecuredRichTextNullable, - SecuredString, + SecuredStringNullable, Sensitivity, SensitivityField, UnsecuredDto, @@ -117,17 +117,17 @@ class Engagement extends Interfaces { @Field() // Convert from date to datetime at migration - readonly lastSuspendedAt: SecuredDateTime; + readonly lastSuspendedAt: SecuredDateTimeNullable; @Field() // Convert from date to datetime at migration - readonly lastReactivatedAt: SecuredDateTime; + readonly lastReactivatedAt: SecuredDateTimeNullable; @Field({ description: 'The last time the engagement status was modified', }) // Convert from last terminated/completed at migration - readonly statusModifiedAt: SecuredDateTime; + readonly statusModifiedAt: SecuredDateTimeNullable; @DateTimeField() readonly modifiedAt: DateTime; @@ -181,12 +181,12 @@ export class LanguageEngagement extends Engagement { readonly sentPrintingDate: SecuredDateNullable; @Field() - readonly paratextRegistryId: SecuredString; + readonly paratextRegistryId: SecuredStringNullable; readonly pnp: Secured | null>; @Field() - readonly historicGoal: SecuredString; + readonly historicGoal: SecuredStringNullable; } @RegisterResource({ db: e.InternshipEngagement }) From 9cdb25fbdbf539ea0910f7229701adbba5359fc5 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 11:55:36 -0500 Subject: [PATCH 06/56] Engagement.scope is not directly referenced so don't require it be present It won't be used with EdgeDB Co-authored-by: Bryan Nelson --- src/components/engagement/dto/engagement.dto.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index d7b10094d3..192f345a38 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -25,7 +25,6 @@ import { import { BaseNode } from '~/core/database/results'; import { e } from '~/core/edgedb'; import { LinkTo, RegisterResource } from '~/core/resources'; -import { ScopedRole } from '../../authorization/dto'; import { ChangesetAware } from '../../changeset/dto'; import { Product, SecuredMethodologies } from '../../product/dto'; import { @@ -132,10 +131,6 @@ class Engagement extends Interfaces { @DateTimeField() readonly modifiedAt: DateTime; - // A list of non-global roles the requesting user has available for this object. - // This is just a cache, to prevent extra db lookups within the same request. - declare readonly scope: ScopedRole[]; - @Field() readonly description: SecuredRichTextNullable; } From 27eb95762219086c57be751cc6dd57e7f01508ea Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 12:17:28 -0500 Subject: [PATCH 07/56] Refactor EngagementService Move constraints & separate reads/writes into neo4j repo. Make `secure` sync. Remove unnecessary logs & try/catches & comments. Co-authored-by: Bryan Nelson --- .../engagement/engagement.repository.ts | 229 ++++++++++++--- .../engagement/engagement.service.ts | 266 +++--------------- .../handlers/set-initial-end-date.handler.ts | 48 +--- 3 files changed, 241 insertions(+), 302 deletions(-) diff --git a/src/components/engagement/engagement.repository.ts b/src/components/engagement/engagement.repository.ts index 9bafce92cf..03054111c6 100644 --- a/src/components/engagement/engagement.repository.ts +++ b/src/components/engagement/engagement.repository.ts @@ -12,8 +12,10 @@ import { difference, pickBy, upperFirst } from 'lodash'; import { DateTime } from 'luxon'; import { MergeExclusive } from 'type-fest'; import { + DuplicateException, generateId, ID, + InputException, labelForView, NotFoundException, ObjectView, @@ -24,7 +26,7 @@ import { viewOfChangeset, } from '~/common'; import { CommonRepository, OnIndex } from '~/core/database'; -import { ChangesOf, getChanges } from '~/core/database/changes'; +import { getChanges } from '~/core/database/changes'; import { ACTIVE, coalesce, @@ -46,11 +48,13 @@ import { whereNotDeletedInChangeset, } from '~/core/database/query'; import { Privileges } from '../authorization'; +import { FileService } from '../file'; import { FileId } from '../file/dto'; import { languageFilters, languageSorters, } from '../language/language.repository'; +import { Location } from '../location/dto'; import { matchCurrentDue, progressReportSorters, @@ -59,6 +63,7 @@ import { ProjectType } from '../project/dto'; import { projectFilters } from '../project/project-filters.query'; import { projectSorters } from '../project/project.repository'; import { userFilters } from '../user'; +import { User } from '../user/dto'; import { CreateInternshipEngagement, CreateLanguageEngagement, @@ -80,7 +85,10 @@ export type LanguageOrEngagementId = MergeExclusive< @Injectable() export class EngagementRepository extends CommonRepository { - constructor(private readonly privileges: Privileges) { + constructor( + private readonly privileges: Privileges, + private readonly files: FileService, + ) { super(); } @@ -192,10 +200,9 @@ export class EngagementRepository extends CommonRepository { ); } - // CREATE /////////////////////////////////////////////////////////// - async createLanguageEngagement( input: CreateLanguageEngagement, + session: Session, changeset?: ID, ) { const pnpId = await generateId(); @@ -219,6 +226,17 @@ export class EngagementRepository extends CommonRepository { canDelete: true, }; + await this.verifyRelationshipEligibility( + projectId, + languageId, + false, + changeset, + ); + + if (input.firstScripture) { + await this.verifyFirstScripture({ languageId }); + } + const query = this.db .query() .apply(await createNode(LanguageEngagement, { initialProps })) @@ -238,11 +256,26 @@ export class EngagementRepository extends CommonRepository { throw new ServerException('Could not create Language Engagement'); } - return { id: result.id, pnpId }; + await this.files.createDefinedFile( + pnpId, + `PNP`, + session, + result.id, + 'pnp', + input.pnp, + 'engagement.pnp', + ); + + return (await this.readOne( + result.id, + session, + viewOfChangeset(changeset), + )) as UnsecuredDto; } async createInternshipEngagement( input: CreateInternshipEngagement, + session: Session, changeset?: ID, ) { const growthPlanId = await generateId(); @@ -268,6 +301,13 @@ export class EngagementRepository extends CommonRepository { canDelete: true, }; + await this.verifyRelationshipEligibility( + projectId, + internId, + true, + changeset, + ); + const query = this.db .query() .apply(await createNode(InternshipEngagement, { initialProps })) @@ -287,67 +327,124 @@ export class EngagementRepository extends CommonRepository { .return<{ id: ID }>('node.id as id'); const result = await query.first(); if (!result) { - throw new NotFoundException(); + if (mentorId && !(await this.getBaseNode(mentorId, User))) { + throw new NotFoundException( + 'Could not find mentor', + 'engagement.mentorId', + ); + } + + if ( + countryOfOriginId && + !(await this.getBaseNode(countryOfOriginId, Location)) + ) { + throw new NotFoundException( + 'Could not find country of origin', + 'engagement.countryOfOriginId', + ); + } + + throw new ServerException('Could not create Internship Engagement'); } - return { id: result.id, growthPlanId }; + await this.files.createDefinedFile( + growthPlanId, + `Growth Plan`, + session, + result.id, + 'growthPlan', + input.growthPlan, + 'engagement.growthPlan', + ); + + return (await this.readOne( + result.id, + session, + viewOfChangeset(changeset), + )) as UnsecuredDto; } - // UPDATE /////////////////////////////////////////////////////////// - getActualLanguageChanges = getChanges(LanguageEngagement); async updateLanguage( - existing: LanguageEngagement | UnsecuredDto, - changes: ChangesOf, + changes: UpdateLanguageEngagement, + session: Session, changeset?: ID, - ): Promise { - const { pnp, ...simpleChanges } = changes; + ) { + const { id, pnp, ...simpleChanges } = changes; + + if (pnp) { + const engagement = await this.readOne(id, session); + if (engagement.pnp) { + await this.files.createFileVersion( + { + ...pnp, + parentId: engagement.pnp.id, + }, + session, + ); + } + } + + if (changes.firstScripture) { + await this.verifyFirstScripture({ engagementId: id }); + } await this.db.updateProperties({ type: LanguageEngagement, - object: existing, + object: { id }, changes: simpleChanges, changeset, }); + + return await this.readOne(id, session); } getActualInternshipChanges = getChanges(InternshipEngagement); async updateInternship( - existing: InternshipEngagement | UnsecuredDto, - changes: ChangesOf, + changes: UpdateInternshipEngagement, + session: Session, changeset?: ID, - ): Promise { - const { - mentorId, - countryOfOriginId, - growthPlan: _, - ...simpleChanges - } = changes; + ) { + const { id, mentorId, countryOfOriginId, growthPlan, ...simpleChanges } = + changes; + + if (growthPlan) { + const engagement = await this.readOne(id, session); + if (engagement.growthPlan) { + await this.files.createFileVersion( + { + ...growthPlan, + parentId: engagement.growthPlan.id, + }, + session, + ); + } + } if (mentorId !== undefined) { - await this.updateRelation('mentor', 'User', existing.id, mentorId); + await this.updateRelation('mentor', 'User', id, mentorId); } if (countryOfOriginId !== undefined) { await this.updateRelation( 'countryOfOrigin', 'Location', - existing.id, + id, countryOfOriginId, ); } await this.db.updateProperties({ type: InternshipEngagement, - object: existing, + object: { id }, changes: simpleChanges, changeset, }); - } - // LIST /////////////////////////////////////////////////////////// + return await this.readOne(id, session); + } async list(input: EngagementListInput, session: Session, changeset?: ID) { const result = await this.db @@ -425,18 +522,19 @@ export class EngagementRepository extends CommonRepository { return rows.map((r) => r.id); } - async verifyRelationshipEligibility( + protected async verifyRelationshipEligibility( projectId: ID, otherId: ID, - isTranslation: boolean, - property: 'language' | 'intern', + isInternship: boolean, changeset?: ID, ) { - return await this.db + const property = isInternship ? 'intern' : 'language'; + + const result = await this.db .query() .optionalMatch(node('project', 'Project', { id: projectId })) .optionalMatch( - node('other', isTranslation ? 'Language' : 'User', { + node('other', !isInternship ? 'Language' : 'User', { id: otherId, }), ) @@ -465,9 +563,48 @@ export class EngagementRepository extends CommonRepository { engagement?: Node; }>() .first(); + + if (!result?.project) { + throw new NotFoundException( + 'Could not find project', + 'engagement.projectId', + ); + } + + const isActuallyInternship = + result.project.properties.type === ProjectType.Internship; + if (isActuallyInternship !== isInternship) { + throw new InputException( + `Only ${ + isInternship ? 'Internship' : 'Language' + } Engagements can be created on ${ + isInternship ? 'Internship' : 'Translation' + } Projects`, + `engagement.${property}Id`, + ); + } + + const label = isInternship ? 'person' : 'language'; + if (!result?.other) { + throw new NotFoundException( + `Could not find ${label}`, + `engagement.${property}Id`, + ); + } + + if (result.engagement) { + throw new DuplicateException( + `engagement.${property}Id`, + `Engagement for this project and ${label} already exists`, + ); + } + + return result; } - async doesLanguageHaveExternalFirstScripture(id: LanguageOrEngagementId) { + private async doesLanguageHaveExternalFirstScripture( + id: LanguageOrEngagementId, + ) { const result = await this.db .query() .apply(this.matchLanguageOrEngagement(id)) @@ -481,7 +618,9 @@ export class EngagementRepository extends CommonRepository { return !!result; } - async doOtherEngagementsHaveFirstScripture(id: LanguageOrEngagementId) { + private async doOtherEngagementsHaveFirstScripture( + id: LanguageOrEngagementId, + ) { const result = await this.db .query() .apply(this.matchLanguageOrEngagement(id)) @@ -513,6 +652,26 @@ export class EngagementRepository extends CommonRepository { : query.match([node('language', 'Language', { id: languageId })]); } + /** + * if firstScripture is true, validate that the engagement + * is the only engagement for the language that has firstScripture=true + * that the language doesn't have hasExternalFirstScripture=true + */ + private async verifyFirstScripture(id: LanguageOrEngagementId) { + if (await this.doesLanguageHaveExternalFirstScripture(id)) { + throw new InputException( + 'First scripture has already been marked as having been done externally', + 'languageEngagement.firstScripture', + ); + } + if (await this.doOtherEngagementsHaveFirstScripture(id)) { + throw new InputException( + 'Another engagement has already been marked as having done the first scripture', + 'languageEngagement.firstScripture', + ); + } + } + @OnIndex() private createIndexes() { return this.getConstraintsFor(IEngagement); diff --git a/src/components/engagement/engagement.service.ts b/src/components/engagement/engagement.service.ts index 0d4431da9b..045b6f3d60 100644 --- a/src/components/engagement/engagement.service.ts +++ b/src/components/engagement/engagement.service.ts @@ -1,12 +1,9 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { - DuplicateException, ID, InputException, - NotFoundException, ObjectView, SecuredList, - ServerException, Session, UnsecuredDto, viewOfChangeset, @@ -19,16 +16,13 @@ import { Logger, ResourceLoader, } from '~/core'; -import { mapListResults } from '~/core/database/results'; import { Privileges } from '../authorization'; import { CeremonyService } from '../ceremony'; import { FileService } from '../file'; -import { Location } from '../location/dto'; import { ProductService } from '../product'; import { ProductListInput, SecuredProductList } from '../product/dto'; import { ProjectService } from '../project'; -import { IProject, ProjectType } from '../project/dto'; -import { User } from '../user/dto'; +import { IProject } from '../project/dto'; import { CreateInternshipEngagement, CreateLanguageEngagement, @@ -42,10 +36,7 @@ import { UpdateInternshipEngagement, UpdateLanguageEngagement, } from './dto'; -import { - EngagementRepository, - LanguageOrEngagementId, -} from './engagement.repository'; +import { EngagementRepository } from './engagement.repository'; import { EngagementRules } from './engagement.rules'; import { EngagementCreatedEvent, @@ -76,53 +67,19 @@ export class EngagementService { session: Session, changeset?: ID, ): Promise { - const { languageId, projectId } = input; - - await this.verifyRelationshipEligibility( - projectId, - languageId, - false, - changeset, - ); - - await this.verifyCreateEngagement(projectId, session); - - if (input.firstScripture) { - await this.verifyFirstScripture({ languageId }); - } - + await this.verifyCreateEngagement(input.projectId, session); this.verifyCreationStatus(input.status); - this.logger.debug('Creating language engagement', { + const engagement = await this.repo.createLanguageEngagement( input, - userId: session.userId, - }); - - const { id, pnpId } = await this.repo.createLanguageEngagement( - input, - changeset, - ); - - await this.files.createDefinedFile( - pnpId, - `PNP`, - session, - id, - 'pnp', - input.pnp, - 'engagement.pnp', - ); - - const engagement = await this.repo.readOne( - id, session, - viewOfChangeset(changeset), + changeset, ); const event = new EngagementCreatedEvent(engagement, input, session); await this.eventBus.publish(event); - return (await this.secure(event.engagement, session)) as LanguageEngagement; + return this.secure(event.engagement, session) as LanguageEngagement; } async createInternshipEngagement( @@ -130,61 +87,13 @@ export class EngagementService { session: Session, changeset?: ID, ): Promise { - const { projectId, internId, mentorId, countryOfOriginId } = input; - - await this.verifyRelationshipEligibility( - projectId, - internId, - true, - changeset, - ); - - await this.verifyCreateEngagement(projectId, session); - + await this.verifyCreateEngagement(input.projectId, session); this.verifyCreationStatus(input.status); - this.logger.debug('Creating internship engagement', { + const { id } = await this.repo.createInternshipEngagement( input, - userId: session.userId, - }); - - let id; - let growthPlanId; - try { - ({ id, growthPlanId } = await this.repo.createInternshipEngagement( - input, - changeset, - )); - } catch (e) { - if (!(e instanceof NotFoundException)) { - throw e; - } - if (mentorId && !(await this.repo.getBaseNode(mentorId, User))) { - throw new NotFoundException( - 'Could not find mentor', - 'engagement.mentorId', - ); - } - if ( - countryOfOriginId && - !(await this.repo.getBaseNode(countryOfOriginId, Location)) - ) { - throw new NotFoundException( - 'Could not find country of origin', - 'engagement.countryOfOriginId', - ); - } - throw new ServerException('Could not create Internship Engagement', e); - } - - await this.files.createDefinedFile( - growthPlanId, - `Growth Plan`, session, - id, - 'growthPlan', - input.growthPlan, - 'engagement.growthPlan', + changeset, ); const engagement = await this.repo.readOne( @@ -196,10 +105,7 @@ export class EngagementService { const event = new EngagementCreatedEvent(engagement, input, session); await this.eventBus.publish(event); - return (await this.secure( - event.engagement, - session, - )) as InternshipEngagement; + return this.secure(event.engagement, session) as InternshipEngagement; } private async verifyCreateEngagement(projectId: ID, session: Session) { @@ -221,47 +127,31 @@ export class EngagementService { } } - // READ /////////////////////////////////////////////////////////// - @HandleIdLookup([LanguageEngagement, InternshipEngagement]) async readOne( id: ID, session: Session, view?: ObjectView, ): Promise { - this.logger.debug('readOne', { id, userId: session.userId }); - if (!id) { - throw new NotFoundException('no id given', 'engagement.id'); - } const dto = await this.repo.readOne(id, session, view); - return await this.secure(dto, session); + return this.secure(dto, session); } async readMany(ids: readonly ID[], session: Session, view?: ObjectView) { const engagements = await this.repo.readMany(ids, session, view); - return await Promise.all( - engagements.map((dto) => this.secure(dto, session)), - ); + return engagements.map((dto) => this.secure(dto, session)); } - async secure( - dto: UnsecuredDto, - session: Session, - ): Promise { + secure(dto: UnsecuredDto, session: Session): Engagement { return this.privileges.for(session, resolveEngagementType(dto)).secure(dto); } - // UPDATE //////////////////////////////////////////////////////// - async updateLanguageEngagement( input: UpdateLanguageEngagement, session: Session, changeset?: ID, ): Promise { const view: ObjectView = viewOfChangeset(changeset); - if (input.firstScripture) { - await this.verifyFirstScripture({ engagementId: input.id }); - } if (input.status) { await this.engagementRules.verifyStatusChange( @@ -273,7 +163,7 @@ export class EngagementService { } const previous = await this.repo.readOne(input.id, session, view); - const object = (await this.secure(previous, session)) as LanguageEngagement; + const object = this.secure(previous, session) as LanguageEngagement; const { methodology: _, ...maybeChanges } = input; const changes = this.repo.getActualLanguageChanges(object, maybeChanges); @@ -281,25 +171,19 @@ export class EngagementService { .for(session, LanguageEngagement, object) .verifyChanges(changes); - await this.files.updateDefinedFile( - object.pnp, - 'engagement.pnp', - changes.pnp, + const updated = await this.repo.updateLanguage( + { + id: object.id, + ...changes, + }, session, + changeset, ); - await this.repo.updateLanguage(object, changes, changeset); - - const updated = (await this.repo.readOne( - input.id, - session, - view, - )) as UnsecuredDto; - const event = new EngagementUpdatedEvent(updated, previous, input, session); await this.eventBus.publish(event); - return (await this.secure(event.updated, session)) as LanguageEngagement; + return this.secure(event.updated, session) as LanguageEngagement; } async updateInternshipEngagement( @@ -308,6 +192,7 @@ export class EngagementService { changeset?: ID, ): Promise { const view: ObjectView = viewOfChangeset(changeset); + if (input.status) { await this.engagementRules.verifyStatusChange( input.id, @@ -318,10 +203,7 @@ export class EngagementService { } const previous = await this.repo.readOne(input.id, session, view); - const object = (await this.secure( - previous, - session, - )) as InternshipEngagement; + const object = this.secure(previous, session) as InternshipEngagement; const changes = this.repo.getActualInternshipChanges(object, input); this.privileges @@ -335,18 +217,16 @@ export class EngagementService { session, ); - await this.repo.updateInternship(object, changes, changeset); - - const updated = (await this.repo.readOne( - input.id, + const updated = await this.repo.updateInternship( + { id: object.id, ...changes }, session, - view, - )) as UnsecuredDto; + changeset, + ); const event = new EngagementUpdatedEvent(updated, previous, input, session); await this.eventBus.publish(event); - return (await this.secure(event.updated, session)) as InternshipEngagement; + return this.secure(event.updated, session) as InternshipEngagement; } async triggerUpdateEvent(id: ID, session: Session) { @@ -355,8 +235,6 @@ export class EngagementService { await this.eventBus.publish(event); } - // DELETE ///////////////////////////////////////////////////////// - async delete(id: ID, session: Session, changeset?: ID): Promise { const object = await this.readOne(id, session); @@ -365,19 +243,9 @@ export class EngagementService { .verifyCan('delete'); await this.eventBus.publish(new EngagementWillDeleteEvent(object, session)); - - try { - await this.repo.deleteNode(object, { changeset }); - } catch (e) { - this.logger.warning('Failed to delete Engagement', { - exception: e, - }); - throw new ServerException('Failed to delete Engagement'); - } + await this.repo.deleteNode(object, { changeset }); } - // LIST /////////////////////////////////////////////////////////// - async list( input: EngagementListInput, session: Session, @@ -387,7 +255,10 @@ export class EngagementService { // if that ever changes, create a limitedScope and add to the list function. const results = await this.repo.list(input, session, view?.changeset); - return await mapListResults(results, (dto) => this.secure(dto, session)); + return { + ...results, + items: results.items.map((dto) => this.secure(dto, session)), + }; } async listAllByProjectId(projectId: ID, session: Session) { @@ -429,75 +300,4 @@ export class EngagementService { const ids = await this.repo.getOngoingEngagementIds(projectId, excludes); return ids.length > 0; } - - protected async verifyRelationshipEligibility( - projectId: ID, - otherId: ID, - isInternship: boolean, - changeset?: ID, - ): Promise { - const property = isInternship ? 'intern' : 'language'; - const result = await this.repo.verifyRelationshipEligibility( - projectId, - otherId, - !isInternship, - property, - changeset, - ); - - if (!result?.project) { - throw new NotFoundException( - 'Could not find project', - 'engagement.projectId', - ); - } - - const isActuallyInternship = - result.project.properties.type === ProjectType.Internship; - if (isActuallyInternship !== isInternship) { - throw new InputException( - `Only ${ - isInternship ? 'Internship' : 'Language' - } Engagements can be created on ${ - isInternship ? 'Internship' : 'Translation' - } Projects`, - `engagement.${property}Id`, - ); - } - - const label = isInternship ? 'person' : 'language'; - if (!result?.other) { - throw new NotFoundException( - `Could not find ${label}`, - `engagement.${property}Id`, - ); - } - - if (result.engagement) { - throw new DuplicateException( - `engagement.${property}Id`, - `Engagement for this project and ${label} already exists`, - ); - } - } - - /** - * if firstScripture is true, validate that the engagement - * is the only engagement for the language that has firstScripture=true - * that the language doesn't have hasExternalFirstScripture=true - */ - protected async verifyFirstScripture(id: LanguageOrEngagementId) { - if (await this.repo.doesLanguageHaveExternalFirstScripture(id)) { - throw new InputException( - 'First scripture has already been marked as having been done externally', - 'languageEngagement.firstScripture', - ); - } - if (await this.repo.doOtherEngagementsHaveFirstScripture(id)) { - throw new InputException( - 'Another engagement has already been marked as having done the first scripture', - 'languageEngagement.firstScripture', - ); - } - } } diff --git a/src/components/engagement/handlers/set-initial-end-date.handler.ts b/src/components/engagement/handlers/set-initial-end-date.handler.ts index 5ce5395c04..0a2c023ea7 100644 --- a/src/components/engagement/handlers/set-initial-end-date.handler.ts +++ b/src/components/engagement/handlers/set-initial-end-date.handler.ts @@ -1,4 +1,4 @@ -import { CalendarDate, ID, ServerException, UnsecuredDto } from '~/common'; +import { ServerException } from '~/common'; import { ConfigService, EventsHandler, @@ -6,12 +6,7 @@ import { ILogger, Logger, } from '~/core'; -import { - Engagement, - EngagementStatus, - InternshipEngagement, - LanguageEngagement, -} from '../dto'; +import { EngagementStatus } from '../dto'; import { EngagementRepository } from '../engagement.repository'; import { EngagementService } from '../engagement.service'; import { EngagementCreatedEvent, EngagementUpdatedEvent } from '../events'; @@ -53,11 +48,19 @@ export class SetInitialEndDate implements IEventHandler { try { const initialEndDate = engagement.endDate; - await this.updateEngagementInitialEndDate( - engagement, - initialEndDate, + const type = + engagement.__typename === 'LanguageEngagement' + ? 'Language' + : 'Internship'; + await this.engagementRepo[`update${type}`]( + { + id: engagement.id, + initialEndDate: initialEndDate || null, + }, + event.session, engagement.changeset, ); + const updatedEngagement = { ...engagement, initialEndDate, @@ -74,32 +77,9 @@ export class SetInitialEndDate implements IEventHandler { exception, }); throw new ServerException( - 'Could set initial end date on engagement', + 'Could not set initial end date on engagement', exception, ); } } - - private async updateEngagementInitialEndDate( - engagement: UnsecuredDto, - initialEndDate: CalendarDate | null | undefined, - changeset?: ID, - ) { - const updateInput = { - initialEndDate: initialEndDate || null, - }; - if (engagement.__typename === 'LanguageEngagement') { - await this.engagementRepo.updateLanguage( - engagement as UnsecuredDto, - updateInput, - changeset, - ); - } else { - await this.engagementRepo.updateInternship( - engagement as UnsecuredDto, - updateInput, - changeset, - ); - } - } } From a924dd5257517da827504421f9587e62d853b1ef Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 12:18:16 -0500 Subject: [PATCH 08/56] Tweak getOngoingEngagementIds to return a readonly array --- src/components/engagement/engagement.repository.ts | 2 +- .../engagement/handlers/update-engagement-status.handler.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/engagement/engagement.repository.ts b/src/components/engagement/engagement.repository.ts index 03054111c6..cad1ae0723 100644 --- a/src/components/engagement/engagement.repository.ts +++ b/src/components/engagement/engagement.repository.ts @@ -502,7 +502,7 @@ export class EngagementRepository extends CommonRepository { async getOngoingEngagementIds( projectId: ID, excludes: EngagementStatus[] = [], - ) { + ): Promise { const rows = await this.db .query() .match([ diff --git a/src/components/engagement/handlers/update-engagement-status.handler.ts b/src/components/engagement/handlers/update-engagement-status.handler.ts index 4589d5419c..97a059e52a 100644 --- a/src/components/engagement/handlers/update-engagement-status.handler.ts +++ b/src/components/engagement/handlers/update-engagement-status.handler.ts @@ -130,7 +130,7 @@ export class UpdateEngagementStatusHandler private async updateEngagements( status: EngagementStatus, - engagementIds: ID[], + engagementIds: readonly ID[], type: ProjectType, session: Session, ) { From 0580dd62ff086734de38b16ea7ec771248e2c813 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Tue, 3 Sep 2024 12:22:51 -0500 Subject: [PATCH 09/56] Loosen test constraint on invalid project references This is compatible with both current messages and new standardized EdgeDB messages Co-authored-by: Bryan Nelson --- test/engagement.e2e-spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/engagement.e2e-spec.ts b/test/engagement.e2e-spec.ts index 3bd68f496a..a10480fc4e 100644 --- a/test/engagement.e2e-spec.ts +++ b/test/engagement.e2e-spec.ts @@ -811,7 +811,9 @@ describe('Engagement e2e', () => { mentorId: mentor.id, }), ).rejects.toThrowGqlError( - errors.notFound({ message: 'Could not find project' }), + errors.notFound({ + message: expect.stringMatching(/Could not find project/i), + }), ); await expect( createInternshipEngagement(app, { @@ -863,7 +865,9 @@ describe('Engagement e2e', () => { languageId: language.id, }), ).rejects.toThrowGqlError( - errors.notFound({ message: 'Could not find project' }), + errors.notFound({ + message: expect.stringMatching(/Could not find project/i), + }), ); await expect( createLanguageEngagement(app, { From 45c5272e203a91639a508b9e3bbc04d088755eb6 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 4 Sep 2024 14:44:37 -0500 Subject: [PATCH 10/56] Merge DB Names into ResourceNameLike --- src/common/id-field.ts | 8 ++------ src/core/resources/resource-name.types.ts | 7 ++++--- src/core/resources/resources.host.ts | 3 +-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/common/id-field.ts b/src/common/id-field.ts index 4a6bf38eae..36d076ad72 100644 --- a/src/common/id-field.ts +++ b/src/common/id-field.ts @@ -2,11 +2,7 @@ import { applyDecorators } from '@nestjs/common'; import { Field, FieldOptions, ID as IDType } from '@nestjs/graphql'; import { ValidationOptions } from 'class-validator'; import { IsAny, IsNever, Tagged } from 'type-fest'; -import type { - AllResourceDBNames, - ResourceName, - ResourceNameLike, -} from '~/core'; +import type { ResourceName, ResourceNameLike } from '~/core'; import { IsId } from './validators'; export const IdField = ({ @@ -40,4 +36,4 @@ type IDTag = IsAny extends true : Kind : never; -type IDKindLike = ResourceNameLike | AllResourceDBNames | object; +type IDKindLike = ResourceNameLike | object; diff --git a/src/core/resources/resource-name.types.ts b/src/core/resources/resource-name.types.ts index ee60438aed..37840ecc01 100644 --- a/src/core/resources/resource-name.types.ts +++ b/src/core/resources/resource-name.types.ts @@ -2,8 +2,9 @@ import { ConditionalKeys, IsAny, LiteralUnion, ValueOf } from 'type-fest'; import { DBName, ResourceShape } from '~/common'; import { ResourceDBMap, ResourceMap } from './map'; -export type AllResourceNames = keyof ResourceMap; +export type AllResourceAppNames = keyof ResourceMap; export type AllResourceDBNames = DBName>; +export type AllResourceNames = AllResourceAppNames | AllResourceDBNames; export type ResourceNameLike = LiteralUnion; //region ResourceName @@ -32,13 +33,13 @@ export type ResourceName< T, IncludeSubclasses extends boolean = false, > = IsAny extends true - ? AllResourceNames // short-circuit and prevent many seemly random circular definitions + ? AllResourceAppNames // short-circuit and prevent many seemly random circular definitions : T extends AllResourceDBNames ? ResourceNameFromStatic< ResourceMap[ResourceNameFromDBName], IncludeSubclasses > - : T extends AllResourceNames + : T extends AllResourceAppNames ? ResourceNameFromStatic : T extends ResourceShape ? ResourceNameFromStatic diff --git a/src/core/resources/resources.host.ts b/src/core/resources/resources.host.ts index 2c31901830..ba56b36ba9 100644 --- a/src/core/resources/resources.host.ts +++ b/src/core/resources/resources.host.ts @@ -14,7 +14,6 @@ import { import { ResourceMap } from './map'; import { __privateDontUseThis } from './resource-map-holder'; import { - AllResourceDBNames, ResourceName, ResourceNameLike, ResourceStaticFromName, @@ -70,7 +69,7 @@ export class ResourcesHost { return this.getByName(name as any); } - getByEdgeDB( + getByEdgeDB( name: Name, ): EnhancedResource< string extends Name From db10842ee32f4efad9390691ab25e96ca4d76c30 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 4 Sep 2024 14:45:21 -0500 Subject: [PATCH 11/56] Change ResourcesHost.getByName to handle DB names --- src/core/resources/resources.host.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/resources/resources.host.ts b/src/core/resources/resources.host.ts index ba56b36ba9..c1b3b11f85 100644 --- a/src/core/resources/resources.host.ts +++ b/src/core/resources/resources.host.ts @@ -4,7 +4,6 @@ import { CachedByArg, mapKeys } from '@seedcompany/common'; import { isObjectType } from 'graphql'; import { LazyGetter as Once } from 'lazy-get-decorator'; import { mapValues } from 'lodash'; -import { ValueOf } from 'type-fest'; import { EnhancedResource, InvalidIdForTypeException, @@ -14,6 +13,7 @@ import { import { ResourceMap } from './map'; import { __privateDontUseThis } from './resource-map-holder'; import { + AllResourceNames, ResourceName, ResourceNameLike, ResourceStaticFromName, @@ -52,17 +52,20 @@ export class ResourcesHost { return mapValues(map, EnhancedResource.of) as any; } - getByName( - name: K, - ): EnhancedResource>> { + getByName( + name: Name, + ): EnhancedResource>> { + if (name.includes('::')) { + return this.getByEdgeDB(name) as any; + } const map = this.getEnhancedMap(); - const resource = map[name]; + const resource = map[name as keyof ResourceMap]; if (!resource) { throw new ServerException( `Unable to determine resource from ResourceMap for type: ${name}`, ); } - return resource; + return resource as any; } getByDynamicName(name: ResourceNameLike): EnhancedResource { From d6f3110c8214482cdb702e697e8ef4bcd8639d69 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 4 Sep 2024 14:53:03 -0500 Subject: [PATCH 12/56] Change ResourceResolver to handle DB names --- .../resources/resource-resolver.service.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/core/resources/resource-resolver.service.ts b/src/core/resources/resource-resolver.service.ts index 5cd30fc6ec..e4597f254f 100644 --- a/src/core/resources/resource-resolver.service.ts +++ b/src/core/resources/resource-resolver.service.ts @@ -7,6 +7,7 @@ import { ID, many, Many, ObjectView, ServerException, Session } from '~/common'; import { BaseNode } from '../database/results'; import { ILogger, Logger } from '../logger'; import { ResourceMap } from './map'; +import { ResourcesHost } from './resources.host'; const RESOLVE_BY_ID = 'RESOLVE_BY_ID'; interface Shape { @@ -48,6 +49,7 @@ export class ResourceResolver { constructor( private readonly discover: DiscoveryService, + private readonly resourcesHost: ResourcesHost, private readonly schemaHost: GraphQLSchemaHost, @Logger('resource-resolver') private readonly logger: ILogger, ) {} @@ -144,13 +146,19 @@ export class ResourceResolver { const names = many(types).map((t) => t.replace(/^Deleted_/, '')); const schema = this.schemaHost.schema; - const resolved = names.filter( - (name) => schema.getType(name) instanceof GraphQLObjectType, - ); + const resolved = names + .flatMap((name) => { + try { + return this.resourcesHost.getByDynamicName(name).name; + } catch (e) { + // Ignore names/`labels` that don't have corresponding resources. + return []; + } + }) + .filter((name) => schema.getType(name) instanceof GraphQLObjectType); if (resolved.length === 1) { - // This is mostly true... - return resolved[0] as keyof ResourceMap; + return resolved[0]; } const namesStr = names.join(', '); From 923444234343600ae3647aff76ed0f9e029733d3 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 5 Sep 2024 09:54:04 -0500 Subject: [PATCH 13/56] Resolve EdgeDB FQNs with `Resource`'s `resolveType` This is an UGLY hack to use ResourcesHost statically --- src/common/resource.dto.ts | 18 ++++++++++++++++-- src/core/resources/resources.host.ts | 4 +++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/common/resource.dto.ts b/src/common/resource.dto.ts index 0f3ed5804a..5ba4ef9b68 100644 --- a/src/common/resource.dto.ts +++ b/src/common/resource.dto.ts @@ -4,7 +4,12 @@ import { LazyGetter as Once } from 'lazy-get-decorator'; import { DateTime } from 'luxon'; import { keys as keysOf } from 'ts-transformer-keys'; import { inspect } from 'util'; -import type { ResourceDBMap, ResourceName } from '~/core'; +import type { + ResourceDBMap, + ResourceLike, + ResourceName, + ResourcesHost, +} from '~/core'; import { $, e } from '~/core/edgedb/reexports'; import { ScopedRole } from '../components/authorization/dto'; import { CalculatedSymbol } from './calculated.decorator'; @@ -27,7 +32,7 @@ const hasTypename = (value: unknown): value is { __typename: string } => export const resolveByTypename = (interfaceName: string) => (value: unknown) => { if (hasTypename(value)) { - return value.__typename; + return EnhancedResource.resolve(value.__typename).name; } throw new ServerException(`Cannot resolve ${interfaceName} type`); }; @@ -87,6 +92,8 @@ export class EnhancedResource> { static readonly dbTypes = new WeakMap, $.$expr_PathNode>(); /** @internal */ static readonly dbSkipAccessPolicies = new Set(); + /** @internal */ + static resourcesHost?: ResourcesHost; private constructor(readonly type: T) {} private static readonly refs = new WeakMap< @@ -94,6 +101,13 @@ export class EnhancedResource> { EnhancedResource >(); + static resolve(ref: ResourceLike) { + if (!EnhancedResource.resourcesHost) { + throw new ServerException('Cannot resolve without ResourcesHost'); + } + return EnhancedResource.resourcesHost.enhance(ref); + } + static of>( resource: T | EnhancedResource, ): EnhancedResource { diff --git a/src/core/resources/resources.host.ts b/src/core/resources/resources.host.ts index c1b3b11f85..16e2e5b472 100644 --- a/src/core/resources/resources.host.ts +++ b/src/core/resources/resources.host.ts @@ -30,7 +30,9 @@ export type ResourceLike = @Injectable() export class ResourcesHost { - constructor(private readonly gqlSchema: GraphQLSchemaHost) {} + constructor(private readonly gqlSchema: GraphQLSchemaHost) { + EnhancedResource.resourcesHost = this; + } getMap() { // @ts-expect-error Yeah we are assuming each type has been correctly From 6cfca45c6ba5349d2e5a5617ed84812e7eada2a6 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 29 Aug 2024 16:33:57 -0500 Subject: [PATCH 14/56] Bump edgedb libs & opt-in to new discriminated unions --- package.json | 4 +-- src/common/resource.dto.ts | 10 ++++++ src/core/edgedb/dto.repository.ts | 6 ++-- src/core/edgedb/index.ts | 9 ++++++ yarn.lock | 52 +++++++++++++++---------------- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index d8b2def34b..de858b12a9 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "cypher-query-builder": "patch:cypher-query-builder@npm%3A6.0.4#~/.yarn/patches/cypher-query-builder-npm-6.0.4-e8707a5e8e.patch", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", - "edgedb": "^1.6.0-canary.20240506T235920", + "edgedb": "^1.6.0-canary.20240827T111834", "execa": "^8.0.1", "express": "^4.18.2", "extensionless": "^1.7.0", @@ -107,7 +107,7 @@ "yaml": "^2.3.3" }, "devDependencies": { - "@edgedb/generate": "^0.6.0-canary.20240506T235941", + "@edgedb/generate": "github:CarsonF/edgedb-js#workspace=@edgedb/generate&head=temp-host", "@nestjs/cli": "^10.2.1", "@nestjs/schematics": "^10.0.3", "@nestjs/testing": "^10.2.7", diff --git a/src/common/resource.dto.ts b/src/common/resource.dto.ts index 5ba4ef9b68..cfb3e6ca1b 100644 --- a/src/common/resource.dto.ts +++ b/src/common/resource.dto.ts @@ -319,7 +319,17 @@ export type DBType> = : never : never; +/** + * The name of the EdgeDB type, it could be abstract. + */ export type DBName = T['__element__']['__name__']; +/** + * The name(s) of the concrete EdgeDB types. + * If the type is abstract, then it is a string union of the concrete type's names. + * If the type is concrete, then it is just the name, just as {@link DBName}. + */ +export type DBNames = + T['__element__']['__polyTypenames__']; export type MaybeUnsecuredInstance> = MaybeSecured>; diff --git a/src/core/edgedb/dto.repository.ts b/src/core/edgedb/dto.repository.ts index 7620cf0d86..653d69ddfc 100644 --- a/src/core/edgedb/dto.repository.ts +++ b/src/core/edgedb/dto.repository.ts @@ -66,7 +66,9 @@ export const RepoFor = < $.ObjectType< DBName, Root['__element__']['__pointers__'], - normaliseShape + normaliseShape, + Root['__element__']['__exclusives__'], + Root['__element__']['__polyTypenames__'] > >; @@ -198,7 +200,7 @@ export const RepoFor = < limit: input.count, })); const query = e.select({ - items, + items: items as any, total: e.count(listOfAllQuery), hasMore: e.op(e.count(thisPage), '>', input.count), }); diff --git a/src/core/edgedb/index.ts b/src/core/edgedb/index.ts index 2401d1d06f..a8ac24b33c 100644 --- a/src/core/edgedb/index.ts +++ b/src/core/edgedb/index.ts @@ -9,3 +9,12 @@ export * from './common.repository'; export * from './dto.repository'; export * from './query-util/disable-access-policies.option'; export * from './query-util/cast-to-enum'; + +declare module './generated-client/typesystem' { + export interface SetTypesystemOptions { + future: { + polymorphismAsDiscriminatedUnions: true; + strictTypeNames: true; + }; + } +} diff --git a/yarn.lock b/yarn.lock index 545d5e7dff..16507a6747 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1485,16 +1485,17 @@ __metadata: languageName: node linkType: hard -"@edgedb/generate@npm:^0.6.0-canary.20240506T235941": - version: 0.6.0-canary.20240506T235941 - resolution: "@edgedb/generate@npm:0.6.0-canary.20240506T235941" +"@edgedb/generate@github:CarsonF/edgedb-js#workspace=@edgedb/generate&head=temp-host": + version: 0.5.5 + resolution: "@edgedb/generate@https://github.com/CarsonF/edgedb-js.git#workspace=%40edgedb%2Fgenerate&commit=4c238572cae9762323c5ed359f399f6c76c3d2e5" dependencies: "@iarna/toml": "npm:^2.2.5" + debug: "npm:^4.3.4" peerDependencies: - edgedb: ^1.5.0 + edgedb: ^1.5.10 bin: generate: dist/cli.js - checksum: 10c0/67f42c695caa0d586f62bd62d1d5ffd810d78bdc60f88d2135cb6fec3f065a31f2e69a45ca8745be05f9f881f22d739f2f8053505354dba497876c3742fb2d8a + checksum: 10c0/b6527c8d92b53be49c4d859b2267224f52651eaf53fed5120a3954c6f1b82b6b1cb8afc4e1d99ee936f02be914d944ca69e39d057d618d456090e19d9756c49a languageName: node linkType: hard @@ -5339,7 +5340,7 @@ __metadata: "@apollo/subgraph": "npm:^2.5.6" "@aws-sdk/client-s3": "npm:^3.440.0" "@aws-sdk/s3-request-presigner": "npm:^3.440.0" - "@edgedb/generate": "npm:^0.6.0-canary.20240506T235941" + "@edgedb/generate": "github:CarsonF/edgedb-js#workspace=@edgedb/generate&head=temp-host" "@faker-js/faker": "npm:^8.2.0" "@ffprobe-installer/ffprobe": "npm:^2.1.2" "@golevelup/nestjs-discovery": "npm:^4.0.0" @@ -5390,7 +5391,7 @@ __metadata: debugger-is-attached: "npm:^1.2.0" dotenv: "npm:^16.3.1" dotenv-expand: "npm:^10.0.0" - edgedb: "npm:^1.6.0-canary.20240506T235920" + edgedb: "npm:^1.6.0-canary.20240827T111834" eslint: "npm:^8.52.0" eslint-plugin-no-only-tests: "npm:^3.1.0" eslint-plugin-typescript-sort-keys: "npm:^2.3.0" @@ -5984,17 +5985,18 @@ __metadata: languageName: node linkType: hard -"edgedb@npm:^1.6.0-canary.20240506T235920": - version: 1.6.0-canary.20240506T235920 - resolution: "edgedb@npm:1.6.0-canary.20240506T235920" +"edgedb@npm:^1.6.0-canary.20240827T111834": + version: 1.6.0-canary.20240827T111834 + resolution: "edgedb@npm:1.6.0-canary.20240827T111834" dependencies: debug: "npm:^4.3.4" env-paths: "npm:^3.0.0" - semver: "npm:^7.6.0" + semver: "npm:^7.6.2" + shell-quote: "npm:^1.8.1" which: "npm:^4.0.0" bin: edgedb: dist/cli.mjs - checksum: 10c0/3bff45a41ac35a1b92a776caedb48d53d568c8e0b4a56f28405490b8e8570f5261b032fcb659914185def3c422c5ae944a5dcc3853d1efaf86b2eec7d60da98c + checksum: 10c0/9bdf05484f2bbffc398bd6ca75628b056baaa51114bbfecc72382fd593619510ae319adfe15879138a90f64ff52ab9b159a87bed0472e05046a0ab19493f257e languageName: node linkType: hard @@ -9407,15 +9409,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9 - languageName: node - linkType: hard - "lru-cache@npm:^7.10.1, lru-cache@npm:^7.14.1, lru-cache@npm:^7.18.3": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" @@ -11772,14 +11765,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": - version: 7.6.0 - resolution: "semver@npm:7.6.0" - dependencies: - lru-cache: "npm:^6.0.0" +"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.2": + version: 7.6.3 + resolution: "semver@npm:7.6.3" bin: semver: bin/semver.js - checksum: 10c0/fbfe717094ace0aa8d6332d7ef5ce727259815bd8d8815700853f4faf23aacbd7192522f0dc5af6df52ef4fa85a355ebd2f5d39f554bd028200d6cf481ab9b53 + checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf languageName: node linkType: hard @@ -11881,6 +11872,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.8.1": + version: 1.8.1 + resolution: "shell-quote@npm:1.8.1" + checksum: 10c0/8cec6fd827bad74d0a49347057d40dfea1e01f12a6123bf82c4649f3ef152fc2bc6d6176e6376bffcd205d9d0ccb4f1f9acae889384d20baff92186f01ea455a + languageName: node + linkType: hard + "shelljs@npm:0.8.5": version: 0.8.5 resolution: "shelljs@npm:0.8.5" From 23f1a6e19ad30b8ef31b26299aac7a563ca45ebc Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 4 Sep 2024 10:30:50 -0500 Subject: [PATCH 15/56] Switch to resolve function w/ class comparison instead of strings --- .../create-engagement-default-ceremony.handler.ts | 3 ++- src/components/engagement/dto/engagement.dto.ts | 1 + src/components/engagement/engagement.resolver.ts | 2 +- .../engagement/events/engagement-created.event.ts | 4 ++-- .../engagement/events/engagement-updated.event.ts | 4 ++-- .../handlers/set-initial-end-date.handler.ts | 4 ++-- .../handlers/set-last-status-date.handler.ts | 11 ++--------- 7 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts b/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts index 86f38788bd..b0ed318546 100644 --- a/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts +++ b/src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts @@ -2,6 +2,7 @@ import { node, relation } from 'cypher-query-builder'; import { DateTime } from 'luxon'; import { ConfigService, EventsHandler, IEventHandler } from '~/core'; import { DatabaseService } from '~/core/database'; +import { LanguageEngagement } from '../../engagement/dto'; import { EngagementCreatedEvent } from '../../engagement/events'; import { CeremonyService } from '../ceremony.service'; import { CeremonyType } from '../dto'; @@ -20,7 +21,7 @@ export class CreateEngagementDefaultCeremonyHandler const { engagement } = event; const input = { type: - engagement.__typename === 'LanguageEngagement' + LanguageEngagement.resolve(engagement) === LanguageEngagement ? CeremonyType.Dedication : CeremonyType.Certification, }; diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index 192f345a38..32c7f8a013 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -63,6 +63,7 @@ class Engagement extends Interfaces { static readonly Props: string[] = keysOf(); static readonly SecuredProps: string[] = keysOf>(); static readonly Parent = import('../../project/dto').then((m) => m.IProject); + static readonly resolve = resolveEngagementType; declare readonly __typename: 'LanguageEngagement' | 'InternshipEngagement'; diff --git a/src/components/engagement/engagement.resolver.ts b/src/components/engagement/engagement.resolver.ts index 1bd0dbf445..baa10a1f77 100644 --- a/src/components/engagement/engagement.resolver.ts +++ b/src/components/engagement/engagement.resolver.ts @@ -60,7 +60,7 @@ export class EngagementResolver { @Loader(EngagementLoader) engagements: LoaderOf, ): Promise { const engagement = await engagements.load(key); - if (engagement.__typename !== 'LanguageEngagement') { + if (LanguageEngagement.resolve(engagement) !== LanguageEngagement) { throw new InvalidIdForTypeException(); } return engagement; diff --git a/src/components/engagement/events/engagement-created.event.ts b/src/components/engagement/events/engagement-created.event.ts index 2bb6a841b8..a47958d3f7 100644 --- a/src/components/engagement/events/engagement-created.event.ts +++ b/src/components/engagement/events/engagement-created.event.ts @@ -18,13 +18,13 @@ export class EngagementCreatedEvent { engagement: UnsecuredDto; input: CreateLanguageEngagement; } { - return this.engagement.__typename === 'LanguageEngagement'; + return LanguageEngagement.resolve(this.engagement) === LanguageEngagement; } isInternshipEngagement(): this is EngagementCreatedEvent & { engagement: UnsecuredDto; input: CreateInternshipEngagement; } { - return this.engagement.__typename === 'InternshipEngagement'; + return LanguageEngagement.resolve(this.engagement) === InternshipEngagement; } } diff --git a/src/components/engagement/events/engagement-updated.event.ts b/src/components/engagement/events/engagement-updated.event.ts index 84b21cc860..19ccfce845 100644 --- a/src/components/engagement/events/engagement-updated.event.ts +++ b/src/components/engagement/events/engagement-updated.event.ts @@ -19,13 +19,13 @@ export class EngagementUpdatedEvent { updated: UnsecuredDto; input: UpdateLanguageEngagement; } { - return this.updated.__typename === 'LanguageEngagement'; + return LanguageEngagement.resolve(this.updated) === LanguageEngagement; } isInternshipEngagement(): this is EngagementUpdatedEvent & { updated: UnsecuredDto; input: UpdateInternshipEngagement; } { - return this.updated.__typename === 'InternshipEngagement'; + return LanguageEngagement.resolve(this.updated) === InternshipEngagement; } } diff --git a/src/components/engagement/handlers/set-initial-end-date.handler.ts b/src/components/engagement/handlers/set-initial-end-date.handler.ts index 0a2c023ea7..4ba0630abb 100644 --- a/src/components/engagement/handlers/set-initial-end-date.handler.ts +++ b/src/components/engagement/handlers/set-initial-end-date.handler.ts @@ -6,7 +6,7 @@ import { ILogger, Logger, } from '~/core'; -import { EngagementStatus } from '../dto'; +import { EngagementStatus, LanguageEngagement } from '../dto'; import { EngagementRepository } from '../engagement.repository'; import { EngagementService } from '../engagement.service'; import { EngagementCreatedEvent, EngagementUpdatedEvent } from '../events'; @@ -49,7 +49,7 @@ export class SetInitialEndDate implements IEventHandler { const initialEndDate = engagement.endDate; const type = - engagement.__typename === 'LanguageEngagement' + LanguageEngagement.resolve(engagement) === LanguageEngagement ? 'Language' : 'Internship'; await this.engagementRepo[`update${type}`]( diff --git a/src/components/engagement/handlers/set-last-status-date.handler.ts b/src/components/engagement/handlers/set-last-status-date.handler.ts index c2e63e9f64..efe99da1c5 100644 --- a/src/components/engagement/handlers/set-last-status-date.handler.ts +++ b/src/components/engagement/handlers/set-last-status-date.handler.ts @@ -7,11 +7,7 @@ import { Logger, } from '~/core'; import { DatabaseService } from '~/core/database'; -import { - EngagementStatus, - InternshipEngagement, - LanguageEngagement, -} from '../dto'; +import { EngagementStatus, IEngagement } from '../dto'; import { EngagementUpdatedEvent } from '../events'; @EventsHandler(EngagementUpdatedEvent) @@ -48,10 +44,7 @@ export class SetLastStatusDate } as const; event.updated = await this.db.updateProperties({ - type: - updated.__typename === 'LanguageEngagement' - ? LanguageEngagement - : InternshipEngagement, + type: IEngagement.resolve(updated), object: updated, changes, }); From ed0696f46d5c645b81bd560032927a360e451e8c Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 4 Sep 2024 11:01:33 -0500 Subject: [PATCH 16/56] Switch to EdgeDB typenames for Engagements --- src/components/engagement/dto/engagement.dto.ts | 9 +++++---- src/components/engagement/engagement.repository.ts | 10 +++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index 32c7f8a013..3315802bff 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -7,6 +7,7 @@ import { DateInterval, DateTimeField, DbLabel, + DBNames, IntersectTypes, parentIdMiddleware, Resource, @@ -47,7 +48,7 @@ export type AnyEngagement = MergeExclusive< const Interfaces = IntersectTypes(Resource, ChangesetAware); export const resolveEngagementType = (val: Pick) => - val.__typename === 'LanguageEngagement' + val.__typename === 'default::LanguageEngagement' ? LanguageEngagement : InternshipEngagement; @@ -65,7 +66,7 @@ class Engagement extends Interfaces { static readonly Parent = import('../../project/dto').then((m) => m.IProject); static readonly resolve = resolveEngagementType; - declare readonly __typename: 'LanguageEngagement' | 'InternshipEngagement'; + declare readonly __typename: DBNames; readonly project: LinkTo<'Project'> & Pick; @@ -155,7 +156,7 @@ export class LanguageEngagement extends Engagement { (m) => m.TranslationProject, ); - declare readonly __typename: 'LanguageEngagement'; + declare readonly __typename: DBNames; @Field(() => TranslationProject) declare readonly parent: BaseNode; @@ -196,7 +197,7 @@ export class InternshipEngagement extends Engagement { (m) => m.InternshipProject, ); - declare readonly __typename: 'InternshipEngagement'; + declare readonly __typename: DBNames; @Field(() => InternshipProject) declare readonly parent: BaseNode; diff --git a/src/components/engagement/engagement.repository.ts b/src/components/engagement/engagement.repository.ts index cad1ae0723..9c4bb4cc04 100644 --- a/src/components/engagement/engagement.repository.ts +++ b/src/components/engagement/engagement.repository.ts @@ -36,6 +36,7 @@ import { filter, FullTextIndex, INACTIVE, + listConcat, matchChangesetAndChangedProps, matchProjectSens, matchPropsAndProjectSensAndScopedRoles, @@ -169,9 +170,12 @@ export class EngagementRepository extends CommonRepository { ]) .return<{ dto: UnsecuredDto }>( merge('props', 'changedProps', { - __typename: typenameForView( - ['LanguageEngagement', 'InternshipEngagement'], - view, + __typename: listConcat( + '"default::"', + typenameForView( + ['LanguageEngagement', 'InternshipEngagement'], + view, + ), ), parent: 'project', project: { From d4c7e57dbd7ebe97e9ca4683c45d8af0eaeb80ca Mon Sep 17 00:00:00 2001 From: Bryan Nelson Date: Tue, 11 Jun 2024 14:28:59 -0400 Subject: [PATCH 17/56] Add trigger assertions for `firstScripture` in `Engagement` schema --- dbschema/engagement.esdl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/dbschema/engagement.esdl b/dbschema/engagement.esdl index ee2e13c016..58c9d0346b 100644 --- a/dbschema/engagement.esdl +++ b/dbschema/engagement.esdl @@ -51,7 +51,20 @@ module default { property firstScripture := ( exists .language.firstScriptureEngagement ); - + + trigger denyDuplicateFirstScriptureBasedOnExternal after insert, update for each do ( + assert( + not __new__.firstScripture or not exists __new__.language.hasExternalFirstScripture, + message := "First scripture has already been marked as having been done externally" + ) + ); + trigger denyDuplicateFirstScriptureBasedOnOtherEngagement after insert, update for each do ( + assert( + not exists (select __new__.language.engagements filter .firstScripture), + message := "Another engagement has already been marked as having done the first scripture" + ) + ); + required lukePartnership: bool { default := false; }; From ff509e4675acb12fbfb30ddaceb3a639082fd72e Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 5 Sep 2024 08:47:08 -0500 Subject: [PATCH 18/56] [EdgeDB] Engagement repo w/ polymorphic hydration --- .../engagement/dto/engagement.dto.ts | 5 + .../engagement.edgedb.repository.ts | 130 ++++++++++++++++++ .../engagement/engagement.module.ts | 4 +- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/components/engagement/engagement.edgedb.repository.ts diff --git a/src/components/engagement/dto/engagement.dto.ts b/src/components/engagement/dto/engagement.dto.ts index 3315802bff..627cc340a8 100644 --- a/src/components/engagement/dto/engagement.dto.ts +++ b/src/components/engagement/dto/engagement.dto.ts @@ -222,6 +222,11 @@ export class InternshipEngagement extends Engagement { export const engagementRange = (engagement: UnsecuredDto) => DateInterval.tryFrom(engagement.startDate, engagement.endDate); +export const EngagementConcretes = { + LanguageEngagement, + InternshipEngagement, +}; + declare module '~/core/resources/map' { interface ResourceMap { Engagement: typeof Engagement; diff --git a/src/components/engagement/engagement.edgedb.repository.ts b/src/components/engagement/engagement.edgedb.repository.ts new file mode 100644 index 0000000000..f4235dc01f --- /dev/null +++ b/src/components/engagement/engagement.edgedb.repository.ts @@ -0,0 +1,130 @@ +import { Injectable, Type } from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; +import { LazyGetter } from 'lazy-get-decorator'; +import { PublicOf } from '~/common'; +import { grabInstances } from '~/common/instance-maps'; +import { e, RepoFor } from '~/core/edgedb'; +import { + EngagementConcretes as ConcreteTypes, + CreateInternshipEngagement, + CreateLanguageEngagement, + IEngagement, + UpdateInternshipEngagement, + UpdateLanguageEngagement, +} from './dto'; +import { EngagementRepository } from './engagement.repository'; + +const baseHydrate = e.shape(e.Engagement, (engagement) => ({ + ...engagement['*'], + __typename: engagement.__type__.name, + project: { + id: true, + status: true, + type: true, + }, + parent: e.tuple({ + identity: engagement.project.id, + labels: e.array_agg(e.set(engagement.project.__type__.name.slice(9, null))), + properties: e.tuple({ + id: engagement.project.id, + createdAt: engagement.project.createdAt, + }), + }), + ceremony: true, + completeDate: engagement.completedDate, // TODO fix in schema +})); + +const languageExtraHydrate = { + language: true, + firstScripture: true, + lukePartnership: true, + openToInvestorVisit: true, + sentPrintingDate: true, + paratextRegistryId: true, + pnp: true, + historicGoal: true, +} as const; + +const internshipExtraHydrate = { + countryOfOrigin: true, + intern: true, + mentor: true, + position: true, + methodologies: true, + growthPlan: true, +} as const; + +const languageHydrate = e.shape(e.LanguageEngagement, (le) => ({ + ...baseHydrate(le), + __typename: le.__type__.name, + ...languageExtraHydrate, +})); + +const internshipHydrate = e.shape(e.InternshipEngagement, (ie) => ({ + ...baseHydrate(ie), + __typename: ie.__type__.name, + ...internshipExtraHydrate, +})); + +const hydrate = e.shape(e.Engagement, (engagement) => ({ + ...baseHydrate(engagement), + ...e.is(e.LanguageEngagement, languageExtraHydrate), + ...e.is(e.InternshipEngagement, internshipExtraHydrate), +})); + +export const ConcreteRepos = { + LanguageEngagement: class LanguageEngagementRepository extends RepoFor( + ConcreteTypes.LanguageEngagement, + { + hydrate: languageHydrate, + }, + ) {}, + + InternshipEngagement: class InternshipEngagementRepository extends RepoFor( + ConcreteTypes.InternshipEngagement, + { + hydrate: internshipHydrate, + }, + ) {}, +} satisfies Record; + +@Injectable() +export class EngagementEdgeDBRepository + extends RepoFor(IEngagement, { + hydrate, + omit: ['create', 'update'], + }) + implements PublicOf +{ + constructor(private readonly moduleRef: ModuleRef) { + super(); + } + + @LazyGetter() protected get concretes() { + return grabInstances(this.moduleRef, ConcreteRepos); + } + + async createLanguageEngagement(input: CreateLanguageEngagement) { + return await this.concretes.LanguageEngagement.create(input); + } + + async createInternshipEngagement(input: CreateInternshipEngagement) { + return await this.concretes.InternshipEngagement.create(input); + } + + get getActualLanguageChanges() { + return this.concretes.LanguageEngagement.getActualChanges; + } + + get getActualInternshipChanges() { + return this.concretes.InternshipEngagement.getActualChanges; + } + + async updateLanguage(input: UpdateLanguageEngagement) { + return await this.concretes.LanguageEngagement.update(input); + } + + async updateInternship(input: UpdateInternshipEngagement) { + return await this.concretes.InternshipEngagement.update(input); + } +} diff --git a/src/components/engagement/engagement.module.ts b/src/components/engagement/engagement.module.ts index d38e7acb2e..a4a8aaca21 100644 --- a/src/components/engagement/engagement.module.ts +++ b/src/components/engagement/engagement.module.ts @@ -1,4 +1,5 @@ import { forwardRef, Module } from '@nestjs/common'; +import { splitDb } from '~/core'; import { AuthorizationModule } from '../authorization/authorization.module'; import { CeremonyModule } from '../ceremony/ceremony.module'; import { FileModule } from '../file/file.module'; @@ -7,6 +8,7 @@ import { LocationModule } from '../location/location.module'; import { ProductModule } from '../product/product.module'; import { ProjectModule } from '../project/project.module'; import { EngagementStatusResolver } from './engagement-status.resolver'; +import { EngagementEdgeDBRepository } from './engagement.edgedb.repository'; import { EngagementLoader } from './engagement.loader'; import { EngagementRepository } from './engagement.repository'; import { EngagementResolver } from './engagement.resolver'; @@ -38,7 +40,7 @@ import { EngagementProductConnectionResolver } from './product-connection.resolv EngagementProductConnectionResolver, EngagementRules, EngagementService, - EngagementRepository, + splitDb(EngagementRepository, EngagementEdgeDBRepository), EngagementLoader, ...Object.values(handlers), FixNullMethodologiesMigration, From 19e827042133e1ed2e79f8c115d800c9151c3c85 Mon Sep 17 00:00:00 2001 From: Bryan Nelson Date: Thu, 5 Sep 2024 08:49:11 -0500 Subject: [PATCH 19/56] [EdgeDB] Finish Engagement queries --- .../engagement.edgedb.repository.ts | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/components/engagement/engagement.edgedb.repository.ts b/src/components/engagement/engagement.edgedb.repository.ts index f4235dc01f..9e18865a5d 100644 --- a/src/components/engagement/engagement.edgedb.repository.ts +++ b/src/components/engagement/engagement.edgedb.repository.ts @@ -1,7 +1,8 @@ import { Injectable, Type } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { LazyGetter } from 'lazy-get-decorator'; -import { PublicOf } from '~/common'; +import { difference } from 'lodash'; +import { ID, PublicOf } from '~/common'; import { grabInstances } from '~/common/instance-maps'; import { e, RepoFor } from '~/core/edgedb'; import { @@ -9,6 +10,7 @@ import { CreateInternshipEngagement, CreateLanguageEngagement, IEngagement, + EngagementStatus as Status, UpdateInternshipEngagement, UpdateLanguageEngagement, } from './dto'; @@ -77,15 +79,33 @@ export const ConcreteRepos = { ConcreteTypes.LanguageEngagement, { hydrate: languageHydrate, + omit: ['create'], }, - ) {}, + ) { + async create(input: CreateLanguageEngagement) { + const project = e.cast(e.TranslationProject, e.uuid(input.projectId)); + return await this.defaults.create({ + ...input, + projectContext: project.projectContext, + }); + } + }, InternshipEngagement: class InternshipEngagementRepository extends RepoFor( ConcreteTypes.InternshipEngagement, { hydrate: internshipHydrate, + omit: ['create'], }, - ) {}, + ) { + async create(input: CreateInternshipEngagement) { + const project = e.cast(e.InternshipProject, e.uuid(input.projectId)); + return await this.defaults.create({ + ...input, + projectContext: project.projectContext, + }); + } + }, } satisfies Record; @Injectable() @@ -127,4 +147,33 @@ export class EngagementEdgeDBRepository async updateInternship(input: UpdateInternshipEngagement) { return await this.concretes.InternshipEngagement.update(input); } + + async listAllByProjectId(projectId: ID) { + const project = e.cast(e.Project, e.uuid(projectId)); + const query = e.select(e.Engagement, (eng) => ({ + filter: e.op(eng.project, '=', project), + ...hydrate(eng), + })); + + return await this.db.run(query); + } + + async getOngoingEngagementIds(projectId: ID, excludes: Status[] = []) { + const project = e.cast(e.Project, e.uuid(projectId)); + + const ongoingExceptExclusions = e.cast( + e.Engagement.Status, + e.set(...difference([...Status.Ongoing], excludes)), + ); + + const engagements = e.select(e.Engagement, (eng) => ({ + filter: e.op( + e.op(eng.project, '=', project), + 'and', + e.op(eng.status, 'in', ongoingExceptExclusions), + ), + })); + + return await this.db.run(engagements.id); + } } From 8919871e4651016fa5a7610b150d88a85de4700f Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 5 Sep 2024 09:01:36 -0500 Subject: [PATCH 20/56] [EdgeDB] Rename completeDate to match --- dbschema/engagement.esdl | 2 +- dbschema/migrations/00005-m1yeyjr.edgeql | 21 +++++++++++++++++++ .../engagement.edgedb.repository.ts | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 dbschema/migrations/00005-m1yeyjr.edgeql diff --git a/dbschema/engagement.esdl b/dbschema/engagement.esdl index 58c9d0346b..eb2f4657c7 100644 --- a/dbschema/engagement.esdl +++ b/dbschema/engagement.esdl @@ -24,7 +24,7 @@ module default { . ({ }), }), ceremony: true, - completeDate: engagement.completedDate, // TODO fix in schema + completeDate: true, })); const languageExtraHydrate = { From 5726a10934975635f3ae0d72ae05d56553ec280c Mon Sep 17 00:00:00 2001 From: Carson Full Date: Mon, 16 Sep 2024 10:46:41 -0500 Subject: [PATCH 21/56] Register SystemAgent as resource --- src/components/user/dto/actor.dto.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/user/dto/actor.dto.ts b/src/components/user/dto/actor.dto.ts index acaa0338ba..e9a9922e48 100644 --- a/src/components/user/dto/actor.dto.ts +++ b/src/components/user/dto/actor.dto.ts @@ -28,6 +28,10 @@ export class Actor extends DataObject { readonly id: ID; } +@RegisterResource({ + db: e.SystemAgent, + skipAccessPolicies: true, +}) @ObjectType({ implements: [Actor], }) @@ -48,8 +52,10 @@ export class SecuredActor extends SecuredProperty(Actor) {} declare module '~/core/resources/map' { interface ResourceMap { Actor: typeof Actor; + SystemAgent: typeof SystemAgent; } interface ResourceDBMap { Actor: typeof e.Actor; + SystemAgent: typeof e.SystemAgent; } } From 21f736a9e1c2fefb4f9b9216310768d059183e7f Mon Sep 17 00:00:00 2001 From: Andre Turner <79720641+atGit2021@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:58:27 -0500 Subject: [PATCH 22/56] Changed percentage variance for "ahead" (#3285) --- src/components/progress-summary/dto/schedule-status.enum.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/progress-summary/dto/schedule-status.enum.ts b/src/components/progress-summary/dto/schedule-status.enum.ts index b87f03ddb1..608ba9d688 100644 --- a/src/components/progress-summary/dto/schedule-status.enum.ts +++ b/src/components/progress-summary/dto/schedule-status.enum.ts @@ -9,7 +9,7 @@ export const ScheduleStatus = makeEnum({ if (variance > 1 || variance < -1) { throw new Error('Variance should be a decimal between [-1, 1]'); } - return variance > 0.3 + return variance > 0.1 ? status.Ahead : variance < -0.1 ? status.Behind From f78a73140302637726b83b4882f80ef7d5ec7a13 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 19 Sep 2024 14:52:24 -0500 Subject: [PATCH 23/56] Adjust `getParentTypes` to work with `IntersectTypes()` This is possible now that we have our own that adds a static `members` array. --- src/common/parent-types.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/common/parent-types.ts b/src/common/parent-types.ts index df17138f00..4e4cef4d17 100644 --- a/src/common/parent-types.ts +++ b/src/common/parent-types.ts @@ -1,4 +1,5 @@ -import { AbstractClassType } from './types'; +import { cmpBy } from '@seedcompany/common'; +import { AbstractClassType, IntersectTypes } from './types'; /** * Returns a list of all the parent class types including the given one. @@ -7,6 +8,20 @@ export function getParentTypes( type: AbstractClassType, types: ReadonlyArray> = [type], ): ReadonlyArray> { + // Special handling of classes coming from IntersectTypes() + const members = Object.getOwnPropertyDescriptor(type, 'members')?.value as + | ReturnType['members'] + | undefined; + if (members && Array.isArray(members)) { + const intersectedAncestors = members + .flatMap((member) => [...getParentTypes(member).entries()]) + // Try to maintain order when merging ancestors + .sort(cmpBy(([idx]) => idx)) + .map(([_, type]) => type); + const uniqueAncestors = [...new Set(intersectedAncestors)]; + return [...types, ...uniqueAncestors]; + } + const parent = Object.getPrototypeOf(type); if (parent == null) { // if no parent we are at native Object. We are done but drop the last two @@ -14,5 +29,6 @@ export function getParentTypes( // These are never explicitly declared in user-land code, and we don't care about them. return types.slice(0, -2); } + return getParentTypes(parent, [...types, parent]); } From 444ac6351ab7b309cca3cde8b6e50d614f0a7608 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 19 Sep 2024 15:19:27 -0500 Subject: [PATCH 24/56] Replace the remaining usage of `IntersectionType` with our `IntersectTypes` --- src/common/types.ts | 19 +------------------ src/components/file/media/media.dto.ts | 5 ++--- .../product/dto/update-product.dto.ts | 6 +++--- .../progress-report/media/dto/upload.dto.ts | 2 +- 4 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/common/types.ts b/src/common/types.ts index 4c8d742860..bd34e86c23 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,8 +1,8 @@ import { - IntersectionType as BaseIntersectionType, OmitType as BaseOmitType, PartialType as BasePartialType, PickType as BasePickType, + IntersectionType, } from '@nestjs/graphql'; import { ClassDecoratorFactory } from '@nestjs/graphql/dist/interfaces/class-decorator-factory.interface'; import { AbstractClass, Class } from 'type-fest'; @@ -74,23 +74,6 @@ export const OmitType = BaseOmitType as < decorator?: ClassDecoratorFactory, ) => Class, Args>; -/** - * The IntersectionType() function combines two types into one new type (class). - * - * This just changes the signature to work with abstract classes. - * - * @see https://docs.nestjs.com/graphql/mapped-types#intersection - */ -export const IntersectionType = BaseIntersectionType as < - A, - B, - Args extends unknown[], ->( - classARef: AbstractClass, - classBRef: AbstractClass, - decorator?: ClassDecoratorFactory, -) => Class; - export function IntersectTypes( type1: AbstractClass, ): IntersectedType; diff --git a/src/components/file/media/media.dto.ts b/src/components/file/media/media.dto.ts index 7df7a9f5bf..50305ceb9c 100644 --- a/src/components/file/media/media.dto.ts +++ b/src/components/file/media/media.dto.ts @@ -15,7 +15,7 @@ import { ID, IdField, IdOf, - IntersectionType, + IntersectTypes, NameField, SecuredProps, ServerException, @@ -127,8 +127,7 @@ export class Image extends VisualMedia { @ObjectType({ implements: [VisualMedia, TemporalMedia, Media], }) -@DbLabel('Video', 'VisualMedia', 'TemporalMedia', 'Media') // IntersectionType blocks label inheritance -export class Video extends IntersectionType(VisualMedia, TemporalMedia) { +export class Video extends IntersectTypes(VisualMedia, TemporalMedia) { static readonly Props = keysOf