diff --git a/go.mod b/go.mod index c9fe521d..e2d5f11a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/conductorone/baton-github go 1.25.2 require ( - github.com/conductorone/baton-sdk v0.24.6 + github.com/conductorone/baton-sdk v0.25.1-0.20260825204020-991ca45253a7 github.com/deckarep/golang-set/v2 v2.9.0 github.com/ennyjfrick/ruleguard-logfatal v0.0.2 github.com/golang-jwt/jwt/v5 v5.2.2 diff --git a/go.sum b/go.sum index ceb7ccbd..36c5daee 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8 github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/conductorone/baton-sdk v0.24.6 h1:mORfZrBdsxXSYqZxlGMEQTFf6I2fu2/PBF+0c7a73KU= -github.com/conductorone/baton-sdk v0.24.6/go.mod h1:SKm95z4KkQ23Tufo2ys88lVzbwKb0AQEbKee5GE0Lig= +github.com/conductorone/baton-sdk v0.25.1-0.20260825204020-991ca45253a7 h1:3exONVa6aKJ1pN2sJlTFQbtoWbJeluEvOOQYaaIeN1w= +github.com/conductorone/baton-sdk v0.25.1-0.20260825204020-991ca45253a7/go.mod h1:SKm95z4KkQ23Tufo2ys88lVzbwKb0AQEbKee5GE0Lig= github.com/conductorone/dpop v0.2.6 h1:fakwai/Xm2b/fcDUwJN41WtcSI/2UhQOyRIVvnnrrNA= github.com/conductorone/dpop v0.2.6/go.mod h1:gyo8TtzB9SCFCsjsICH4IaLZ7y64CcrDXMOPBwfq/3s= github.com/conductorone/dpop/integrations/dpop_grpc v0.2.4 h1:lYxYi9/WTSL9sE96CO0QF2BY3kehs8dTTApI134TGCA= diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index b38ed4e8..2d9dabde 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -68,7 +68,7 @@ var ( resourceTypeInvitation = &v2.ResourceType{ Id: "invitation", DisplayName: "Invitation", - // Invitations emit TRAIT_USER with UserTrait_Status_STATUS_UNSPECIFIED. + // Invitations emit TRAIT_USER with STATUS_PENDING. // Accepted members from user.go emit STATUS_ENABLED. Traits: []v2.ResourceType_Trait{ v2.ResourceType_TRAIT_USER, diff --git a/pkg/connector/invitation.go b/pkg/connector/invitation.go index fe49b5c6..7ed54fa6 100644 --- a/pkg/connector/invitation.go +++ b/pkg/connector/invitation.go @@ -64,19 +64,17 @@ func invitationToUserResource(invitation *github.Invitation, status string) (*v2 invitation.GetID(), []resourceSdk.UserTraitOption{ resourceSdk.WithEmail(invitation.GetEmail(), true), - // An invitation is a pending/expired user that must not be - // reported as enabled. WithResourceStatus cannot express this: - // NewUserTrait force-defaults an unset trait status to ENABLED, so - // migrating this line would flip the emitted status from - // UNSPECIFIED to ENABLED. Keep the deprecated trait option (which - // also mirrors UNSPECIFIED to the resource level) to preserve the - // exact status semantics. - //nolint:staticcheck // deliberate: WithResourceStatus would force the trait status to ENABLED; UNSPECIFIED must be preserved for invitations. - resourceSdk.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED), + // Set explicitly: NewUserTrait defaults an unset trait status to + // ENABLED, which an unaccepted invitation is not. + //nolint:staticcheck // trait status is deprecated but must be set to override the ENABLED default. + resourceSdk.WithDetailedStatus(v2.UserTrait_Status_STATUS_PENDING, status), resourceSdk.WithUserLogin(login), }, - // profile has moved from UserTrait to a Resource-level attribute. + // profile and status have moved from UserTrait to Resource-level + // attributes. Expired invitations stay PENDING - they are still not a + // usable account - and carry the distinction in the status details. resourceSdk.WithResourceProfile(profile), + resourceSdk.WithResourceStatus(v2.Status_RESOURCE_STATUS_PENDING, status), ) if err != nil { return nil, err diff --git a/pkg/connector/invitation_test.go b/pkg/connector/invitation_test.go index 9f79e299..dea6e460 100644 --- a/pkg/connector/invitation_test.go +++ b/pkg/connector/invitation_test.go @@ -199,6 +199,7 @@ func TestInvitationListPagination(t *testing.T) { pendingCreated1.Add(invitationLifetime).UTC().Format(time.RFC3339), aliceProfile["invitation_expires_at"], ) + requireInvitationPending(t, byID["1001"], invitationStatusPendingAcceptance) // Expired resources carry status=expired and expires_at = failed_at. daveProfile := invitationProfile(t, byID["2001"]) @@ -207,6 +208,9 @@ func TestInvitationListPagination(t *testing.T) { expiredFailedAt1.UTC().Format(time.RFC3339), daveProfile["invitation_expires_at"], ) + // An expired invitation is still not a usable account, so it stays + // PENDING at both levels; only the details distinguish it. + requireInvitationPending(t, byID["2001"], invitationStatusExpired) }) t.Run("pending 404 falls through to failed", func(t *testing.T) { @@ -222,6 +226,7 @@ func TestInvitationListPagination(t *testing.T) { require.Equal(t, "2001", got[0].Id.Resource) require.Equal(t, invitationStatusExpired, invitationProfile(t, got[0])["invitation_status"]) + requireInvitationPending(t, got[0], invitationStatusExpired) }) t.Run("failed 404 terminates cleanly", func(t *testing.T) { @@ -236,6 +241,7 @@ func TestInvitationListPagination(t *testing.T) { require.Len(t, got, 2) require.Equal(t, invitationStatusPendingAcceptance, invitationProfile(t, got[0])["invitation_status"]) + requireInvitationPending(t, got[0], invitationStatusPendingAcceptance) }) t.Run("both endpoints empty terminates without API errors", func(t *testing.T) { @@ -262,3 +268,21 @@ func invitationProfile(t *testing.T, r *v2.Resource) map[string]any { require.NotNil(t, profile) return profile.AsMap() } + +// requireInvitationPending asserts that an invitation resource reports PENDING +// at both the resource level and the (deprecated) user-trait level, with +// details naming which flavor of pending it is. +func requireInvitationPending(t *testing.T, r *v2.Resource, wantDetails string) { + t.Helper() + + require.Equal(t, v2.Status_RESOURCE_STATUS_PENDING, r.GetStatus().GetStatus()) + require.Equal(t, wantDetails, r.GetStatus().GetDetails()) + + ut, err := resourceSdk.GetUserTrait(r) + require.NoError(t, err) + require.NotNil(t, ut) + //nolint:staticcheck // asserting the deprecated trait status is the point of this check. + require.Equal(t, v2.UserTrait_Status_STATUS_PENDING, ut.GetStatus().GetStatus()) + //nolint:staticcheck // asserting the deprecated trait status is the point of this check. + require.Equal(t, wantDetails, ut.GetStatus().GetDetails()) +} diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait.pb.go index 129f9e71..bd2302aa 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait.pb.go @@ -80,6 +80,9 @@ const ( UserTrait_Status_STATUS_ENABLED UserTrait_Status_Status = 1 UserTrait_Status_STATUS_DISABLED UserTrait_Status_Status = 2 UserTrait_Status_STATUS_DELETED UserTrait_Status_Status = 3 + // Account creation was initiated but the account is not yet usable, such + // as an invitation that has not been accepted. + UserTrait_Status_STATUS_PENDING UserTrait_Status_Status = 4 ) // Enum value maps for UserTrait_Status_Status. @@ -89,12 +92,14 @@ var ( 1: "STATUS_ENABLED", 2: "STATUS_DISABLED", 3: "STATUS_DELETED", + 4: "STATUS_PENDING", } UserTrait_Status_Status_value = map[string]int32{ "STATUS_UNSPECIFIED": 0, "STATUS_ENABLED": 1, "STATUS_DISABLED": 2, "STATUS_DELETED": 3, + "STATUS_PENDING": 4, } ) @@ -2957,7 +2962,7 @@ var File_c1_connector_v2_annotation_trait_proto protoreflect.FileDescriptor const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "\n" + - "&c1/connector/v2/annotation_trait.proto\x12\x0fc1.connector.v2\x1a\x1bc1/connector/v2/asset.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x17validate/validate.proto\"\x9d\v\n" + + "&c1/connector/v2/annotation_trait.proto\x12\x0fc1.connector.v2\x1a\x1bc1/connector/v2/asset.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x17validate/validate.proto\"\xb1\v\n" + "\tUserTrait\x128\n" + "\x06emails\x18\x01 \x03(\v2 .c1.connector.v2.UserTrait.EmailR\x06emails\x12=\n" + "\x06status\x18\x02 \x01(\v2!.c1.connector.v2.UserTrait.StatusB\x02\x18\x01R\x06status\x125\n" + @@ -2980,16 +2985,17 @@ const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "\x05Email\x12!\n" + "\aaddress\x18\x01 \x01(\tB\a\xfaB\x04r\x02`\x01R\aaddress\x12\x1d\n" + "\n" + - "is_primary\x18\x02 \x01(\bR\tisPrimary\x1a\xdc\x01\n" + + "is_primary\x18\x02 \x01(\bR\tisPrimary\x1a\xf0\x01\n" + "\x06Status\x12J\n" + "\x06status\x18\x01 \x01(\x0e2(.c1.connector.v2.UserTrait.Status.StatusB\b\xfaB\x05\x82\x01\x02\x10\x01R\x06status\x12'\n" + "\adetails\x18\x02 \x01(\tB\r\xfaB\n" + - "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"]\n" + + "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"q\n" + "\x06Status\x12\x16\n" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n" + "\x0eSTATUS_ENABLED\x10\x01\x12\x13\n" + "\x0fSTATUS_DISABLED\x10\x02\x12\x12\n" + - "\x0eSTATUS_DELETED\x10\x03\x1a,\n" + + "\x0eSTATUS_DELETED\x10\x03\x12\x12\n" + + "\x0eSTATUS_PENDING\x10\x04\x1a,\n" + "\tMFAStatus\x12\x1f\n" + "\vmfa_enabled\x18\x01 \x01(\bR\n" + "mfaEnabled\x1a,\n" + diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go index 2f33747f..cc912e31 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/annotation_trait_protoopaque.pb.go @@ -80,6 +80,9 @@ const ( UserTrait_Status_STATUS_ENABLED UserTrait_Status_Status = 1 UserTrait_Status_STATUS_DISABLED UserTrait_Status_Status = 2 UserTrait_Status_STATUS_DELETED UserTrait_Status_Status = 3 + // Account creation was initiated but the account is not yet usable, such + // as an invitation that has not been accepted. + UserTrait_Status_STATUS_PENDING UserTrait_Status_Status = 4 ) // Enum value maps for UserTrait_Status_Status. @@ -89,12 +92,14 @@ var ( 1: "STATUS_ENABLED", 2: "STATUS_DISABLED", 3: "STATUS_DELETED", + 4: "STATUS_PENDING", } UserTrait_Status_Status_value = map[string]int32{ "STATUS_UNSPECIFIED": 0, "STATUS_ENABLED": 1, "STATUS_DISABLED": 2, "STATUS_DELETED": 3, + "STATUS_PENDING": 4, } ) @@ -2902,7 +2907,7 @@ var File_c1_connector_v2_annotation_trait_proto protoreflect.FileDescriptor const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "\n" + - "&c1/connector/v2/annotation_trait.proto\x12\x0fc1.connector.v2\x1a\x1bc1/connector/v2/asset.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x17validate/validate.proto\"\x9d\v\n" + + "&c1/connector/v2/annotation_trait.proto\x12\x0fc1.connector.v2\x1a\x1bc1/connector/v2/asset.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x17validate/validate.proto\"\xb1\v\n" + "\tUserTrait\x128\n" + "\x06emails\x18\x01 \x03(\v2 .c1.connector.v2.UserTrait.EmailR\x06emails\x12=\n" + "\x06status\x18\x02 \x01(\v2!.c1.connector.v2.UserTrait.StatusB\x02\x18\x01R\x06status\x125\n" + @@ -2925,16 +2930,17 @@ const file_c1_connector_v2_annotation_trait_proto_rawDesc = "" + "\x05Email\x12!\n" + "\aaddress\x18\x01 \x01(\tB\a\xfaB\x04r\x02`\x01R\aaddress\x12\x1d\n" + "\n" + - "is_primary\x18\x02 \x01(\bR\tisPrimary\x1a\xdc\x01\n" + + "is_primary\x18\x02 \x01(\bR\tisPrimary\x1a\xf0\x01\n" + "\x06Status\x12J\n" + "\x06status\x18\x01 \x01(\x0e2(.c1.connector.v2.UserTrait.Status.StatusB\b\xfaB\x05\x82\x01\x02\x10\x01R\x06status\x12'\n" + "\adetails\x18\x02 \x01(\tB\r\xfaB\n" + - "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"]\n" + + "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"q\n" + "\x06Status\x12\x16\n" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n" + "\x0eSTATUS_ENABLED\x10\x01\x12\x13\n" + "\x0fSTATUS_DISABLED\x10\x02\x12\x12\n" + - "\x0eSTATUS_DELETED\x10\x03\x1a,\n" + + "\x0eSTATUS_DELETED\x10\x03\x12\x12\n" + + "\x0eSTATUS_PENDING\x10\x04\x1a,\n" + "\tMFAStatus\x12\x1f\n" + "\vmfa_enabled\x18\x01 \x01(\bR\n" + "mfaEnabled\x1a,\n" + diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource.pb.go index 487b0dc5..57c9d0c2 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource.pb.go @@ -198,6 +198,9 @@ const ( Status_RESOURCE_STATUS_ENABLED Status_ResourceStatus = 1 Status_RESOURCE_STATUS_DISABLED Status_ResourceStatus = 2 Status_RESOURCE_STATUS_DELETED Status_ResourceStatus = 3 + // Account creation was initiated but the account is not yet usable, such + // as an invitation that has not been accepted. + Status_RESOURCE_STATUS_PENDING Status_ResourceStatus = 4 ) // Enum value maps for Status_ResourceStatus. @@ -207,12 +210,14 @@ var ( 1: "RESOURCE_STATUS_ENABLED", 2: "RESOURCE_STATUS_DISABLED", 3: "RESOURCE_STATUS_DELETED", + 4: "RESOURCE_STATUS_PENDING", } Status_ResourceStatus_value = map[string]int32{ "RESOURCE_STATUS_UNSPECIFIED": 0, "RESOURCE_STATUS_ENABLED": 1, "RESOURCE_STATUS_DISABLED": 2, "RESOURCE_STATUS_DELETED": 3, + "RESOURCE_STATUS_PENDING": 4, } ) @@ -5889,8 +5894,11 @@ func (b0 EncryptionConfig_JWKPublicKeyConfig_builder) Build() *EncryptionConfig_ // supported by the configured age provider. EncryptedData.encrypted_bytes // contains a standard binary age file when this config is used. The provider // sets EncryptedData.key_ids to one lowercase hexadecimal SHA-256 digest of -// the UTF-8 canonical recipient string. It leaves the deprecated -// EncryptedData.key_id empty. +// the UTF-8 canonical recipient string, and leaves the deprecated +// EncryptedData.key_id empty. Rather than reimplement this derivation, +// consumers written in Go should call +// pkg/crypto/providers/age.KeyIDForRecipient, which is the single source of +// truth for the convention. type EncryptionConfig_AgeRecipientConfig struct { state protoimpl.MessageState `protogen:"hybrid.v1"` Recipient string `protobuf:"bytes,1,opt,name=recipient,proto3" json:"recipient,omitempty"` @@ -6184,16 +6192,17 @@ const file_c1_connector_v2_resource_proto_rawDesc = "" + "\x0eCreationSource\x12\x1f\n" + "\x1bCREATION_SOURCE_UNSPECIFIED\x10\x00\x12,\n" + "(CREATION_SOURCE_CONNECTOR_LIST_RESOURCES\x10\x01\x127\n" + - "3CREATION_SOURCE_CONNECTOR_LIST_GRANTS_PRINCIPAL_JIT\x10\x02\"\x87\x02\n" + + "3CREATION_SOURCE_CONNECTOR_LIST_GRANTS_PRINCIPAL_JIT\x10\x02\"\xa4\x02\n" + "\x06Status\x12H\n" + "\x06status\x18\x01 \x01(\x0e2&.c1.connector.v2.Status.ResourceStatusB\b\xfaB\x05\x82\x01\x02\x10\x01R\x06status\x12'\n" + "\adetails\x18\x02 \x01(\tB\r\xfaB\n" + - "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"\x89\x01\n" + + "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"\xa6\x01\n" + "\x0eResourceStatus\x12\x1f\n" + "\x1bRESOURCE_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n" + "\x17RESOURCE_STATUS_ENABLED\x10\x01\x12\x1c\n" + "\x18RESOURCE_STATUS_DISABLED\x10\x02\x12\x1b\n" + - "\x17RESOURCE_STATUS_DELETED\x10\x03\"\xb5\x03\n" + + "\x17RESOURCE_STATUS_DELETED\x10\x03\x12\x1b\n" + + "\x17RESOURCE_STATUS_PENDING\x10\x04\"\xb5\x03\n" + "$ResourcesServiceListResourcesRequest\x124\n" + "\x10resource_type_id\x18\x01 \x01(\tB\n" + "\xfaB\ar\x05 \x01(\x80\bR\x0eresourceTypeId\x12S\n" + diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource_protoopaque.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource_protoopaque.pb.go index d3c999ee..0a7407a6 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource_protoopaque.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connector/v2/resource_protoopaque.pb.go @@ -198,6 +198,9 @@ const ( Status_RESOURCE_STATUS_ENABLED Status_ResourceStatus = 1 Status_RESOURCE_STATUS_DISABLED Status_ResourceStatus = 2 Status_RESOURCE_STATUS_DELETED Status_ResourceStatus = 3 + // Account creation was initiated but the account is not yet usable, such + // as an invitation that has not been accepted. + Status_RESOURCE_STATUS_PENDING Status_ResourceStatus = 4 ) // Enum value maps for Status_ResourceStatus. @@ -207,12 +210,14 @@ var ( 1: "RESOURCE_STATUS_ENABLED", 2: "RESOURCE_STATUS_DISABLED", 3: "RESOURCE_STATUS_DELETED", + 4: "RESOURCE_STATUS_PENDING", } Status_ResourceStatus_value = map[string]int32{ "RESOURCE_STATUS_UNSPECIFIED": 0, "RESOURCE_STATUS_ENABLED": 1, "RESOURCE_STATUS_DISABLED": 2, "RESOURCE_STATUS_DELETED": 3, + "RESOURCE_STATUS_PENDING": 4, } ) @@ -5819,8 +5824,11 @@ func (b0 EncryptionConfig_JWKPublicKeyConfig_builder) Build() *EncryptionConfig_ // supported by the configured age provider. EncryptedData.encrypted_bytes // contains a standard binary age file when this config is used. The provider // sets EncryptedData.key_ids to one lowercase hexadecimal SHA-256 digest of -// the UTF-8 canonical recipient string. It leaves the deprecated -// EncryptedData.key_id empty. +// the UTF-8 canonical recipient string, and leaves the deprecated +// EncryptedData.key_id empty. Rather than reimplement this derivation, +// consumers written in Go should call +// pkg/crypto/providers/age.KeyIDForRecipient, which is the single source of +// truth for the convention. type EncryptionConfig_AgeRecipientConfig struct { state protoimpl.MessageState `protogen:"opaque.v1"` xxx_hidden_Recipient string `protobuf:"bytes,1,opt,name=recipient,proto3"` @@ -6114,16 +6122,17 @@ const file_c1_connector_v2_resource_proto_rawDesc = "" + "\x0eCreationSource\x12\x1f\n" + "\x1bCREATION_SOURCE_UNSPECIFIED\x10\x00\x12,\n" + "(CREATION_SOURCE_CONNECTOR_LIST_RESOURCES\x10\x01\x127\n" + - "3CREATION_SOURCE_CONNECTOR_LIST_GRANTS_PRINCIPAL_JIT\x10\x02\"\x87\x02\n" + + "3CREATION_SOURCE_CONNECTOR_LIST_GRANTS_PRINCIPAL_JIT\x10\x02\"\xa4\x02\n" + "\x06Status\x12H\n" + "\x06status\x18\x01 \x01(\x0e2&.c1.connector.v2.Status.ResourceStatusB\b\xfaB\x05\x82\x01\x02\x10\x01R\x06status\x12'\n" + "\adetails\x18\x02 \x01(\tB\r\xfaB\n" + - "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"\x89\x01\n" + + "r\b \x01(\x80\b\xd0\x01\x01R\adetails\"\xa6\x01\n" + "\x0eResourceStatus\x12\x1f\n" + "\x1bRESOURCE_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n" + "\x17RESOURCE_STATUS_ENABLED\x10\x01\x12\x1c\n" + "\x18RESOURCE_STATUS_DISABLED\x10\x02\x12\x1b\n" + - "\x17RESOURCE_STATUS_DELETED\x10\x03\"\xb5\x03\n" + + "\x17RESOURCE_STATUS_DELETED\x10\x03\x12\x1b\n" + + "\x17RESOURCE_STATUS_PENDING\x10\x04\"\xb5\x03\n" + "$ResourcesServiceListResourcesRequest\x124\n" + "\x10resource_type_id\x18\x01 \x01(\tB\n" + "\xfaB\ar\x05 \x01(\x80\bR\x0eresourceTypeId\x12S\n" + diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.go index fe7c7788..f2606742 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.go @@ -104,7 +104,6 @@ type Task struct { // *Task_ActionGetSchema // *Task_ActionInvoke // *Task_ActionStatus - // *Task_CreateSyncDiff // *Task_CompactSyncs_ // *Task_ListEventFeeds // *Task_ListEvents @@ -332,15 +331,6 @@ func (x *Task) GetActionStatus() *Task_ActionStatusTask { return nil } -func (x *Task) GetCreateSyncDiff() *Task_CreateSyncDiffTask { - if x != nil { - if x, ok := x.TaskType.(*Task_CreateSyncDiff); ok { - return x.CreateSyncDiff - } - } - return nil -} - func (x *Task) GetCompactSyncs() *Task_CompactSyncs { if x != nil { if x, ok := x.TaskType.(*Task_CompactSyncs_); ok { @@ -544,14 +534,6 @@ func (x *Task) SetActionStatus(v *Task_ActionStatusTask) { x.TaskType = &Task_ActionStatus{v} } -func (x *Task) SetCreateSyncDiff(v *Task_CreateSyncDiffTask) { - if v == nil { - x.TaskType = nil - return - } - x.TaskType = &Task_CreateSyncDiff{v} -} - func (x *Task) SetCompactSyncs(v *Task_CompactSyncs) { if v == nil { x.TaskType = nil @@ -747,14 +729,6 @@ func (x *Task) HasActionStatus() bool { return ok } -func (x *Task) HasCreateSyncDiff() bool { - if x == nil { - return false - } - _, ok := x.TaskType.(*Task_CreateSyncDiff) - return ok -} - func (x *Task) HasCompactSyncs() bool { if x == nil { return false @@ -905,12 +879,6 @@ func (x *Task) ClearActionStatus() { } } -func (x *Task) ClearCreateSyncDiff() { - if _, ok := x.TaskType.(*Task_CreateSyncDiff); ok { - x.TaskType = nil - } -} - func (x *Task) ClearCompactSyncs() { if _, ok := x.TaskType.(*Task_CompactSyncs_); ok { x.TaskType = nil @@ -955,7 +923,6 @@ const Task_ActionListSchemas_case case_Task_TaskType = 115 const Task_ActionGetSchema_case case_Task_TaskType = 116 const Task_ActionInvoke_case case_Task_TaskType = 117 const Task_ActionStatus_case case_Task_TaskType = 118 -const Task_CreateSyncDiff_case case_Task_TaskType = 119 const Task_CompactSyncs_case case_Task_TaskType = 120 const Task_ListEventFeeds_case case_Task_TaskType = 121 const Task_ListEvents_case case_Task_TaskType = 122 @@ -1004,8 +971,6 @@ func (x *Task) WhichTaskType() case_Task_TaskType { return Task_ActionInvoke_case case *Task_ActionStatus: return Task_ActionStatus_case - case *Task_CreateSyncDiff: - return Task_CreateSyncDiff_case case *Task_CompactSyncs_: return Task_CompactSyncs_case case *Task_ListEventFeeds: @@ -1044,7 +1009,6 @@ type Task_builder struct { ActionGetSchema *Task_ActionGetSchemaTask ActionInvoke *Task_ActionInvokeTask ActionStatus *Task_ActionStatusTask - CreateSyncDiff *Task_CreateSyncDiffTask CompactSyncs *Task_CompactSyncs ListEventFeeds *Task_ListEventFeedsTask ListEvents *Task_ListEventsTask @@ -1116,9 +1080,6 @@ func (b0 Task_builder) Build() *Task { if b.ActionStatus != nil { x.TaskType = &Task_ActionStatus{b.ActionStatus} } - if b.CreateSyncDiff != nil { - x.TaskType = &Task_CreateSyncDiff{b.CreateSyncDiff} - } if b.CompactSyncs != nil { x.TaskType = &Task_CompactSyncs_{b.CompactSyncs} } @@ -1225,10 +1186,6 @@ type Task_ActionStatus struct { ActionStatus *Task_ActionStatusTask `protobuf:"bytes,118,opt,name=action_status,json=actionStatus,proto3,oneof"` } -type Task_CreateSyncDiff struct { - CreateSyncDiff *Task_CreateSyncDiffTask `protobuf:"bytes,119,opt,name=create_sync_diff,json=createSyncDiff,proto3,oneof"` -} - type Task_CompactSyncs_ struct { CompactSyncs *Task_CompactSyncs `protobuf:"bytes,120,opt,name=compact_syncs,json=compactSyncs,proto3,oneof"` } @@ -1283,8 +1240,6 @@ func (*Task_ActionInvoke) isTask_TaskType() {} func (*Task_ActionStatus) isTask_TaskType() {} -func (*Task_CreateSyncDiff) isTask_TaskType() {} - func (*Task_CompactSyncs_) isTask_TaskType() {} func (*Task_ListEventFeeds) isTask_TaskType() {} @@ -2935,7 +2890,7 @@ type Task_SyncFullTask struct { SyncResourceTypeIds []string `protobuf:"bytes,5,rep,name=sync_resource_type_ids,json=syncResourceTypeIds,proto3" json:"sync_resource_type_ids,omitempty"` // If true, skip syncing grants. Resources and entitlements will still be synced. SkipGrants bool `protobuf:"varint,6,opt,name=skip_grants,json=skipGrants,proto3" json:"skip_grants,omitempty"` - // Storage engine to use for the sync. If empty, the default engine will be used (currently SQLite). + // Storage engine to use for the sync. If empty, the default engine will be used (currently Pebble for new c1z files; existing files keep their on-disk format). StorageEngine string `protobuf:"bytes,7,opt,name=storage_engine,json=storageEngine,proto3" json:"storage_engine,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -3057,7 +3012,7 @@ type Task_SyncFullTask_builder struct { SyncResourceTypeIds []string // If true, skip syncing grants. Resources and entitlements will still be synced. SkipGrants bool - // Storage engine to use for the sync. If empty, the default engine will be used (currently SQLite). + // Storage engine to use for the sync. If empty, the default engine will be used (currently Pebble for new c1z files; existing files keep their on-disk format). StorageEngine string } @@ -4763,12 +4718,16 @@ func (b0 Task_ActionStatusTask_builder) Build() *Task_ActionStatusTask { return m0 } +// Deprecated: diff-sync support was removed from the SDK. The message +// is retained only to satisfy breaking-change detection; nothing +// produces or consumes it. +// +// Deprecated: Marked as deprecated in c1/connectorapi/baton/v1/baton.proto. type Task_CreateSyncDiffTask struct { - state protoimpl.MessageState `protogen:"hybrid.v1"` - // Open to suggestions here - BaseSyncId string `protobuf:"bytes,1,opt,name=base_sync_id,json=baseSyncId,proto3" json:"base_sync_id,omitempty"` - NewSyncId string `protobuf:"bytes,2,opt,name=new_sync_id,json=newSyncId,proto3" json:"new_sync_id,omitempty"` - Annotations []*anypb.Any `protobuf:"bytes,3,rep,name=annotations,proto3" json:"annotations,omitempty"` + state protoimpl.MessageState `protogen:"hybrid.v1"` + BaseSyncId string `protobuf:"bytes,1,opt,name=base_sync_id,json=baseSyncId,proto3" json:"base_sync_id,omitempty"` + NewSyncId string `protobuf:"bytes,2,opt,name=new_sync_id,json=newSyncId,proto3" json:"new_sync_id,omitempty"` + Annotations []*anypb.Any `protobuf:"bytes,3,rep,name=annotations,proto3" json:"annotations,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -4831,10 +4790,10 @@ func (x *Task_CreateSyncDiffTask) SetAnnotations(v []*anypb.Any) { x.Annotations = v } +// Deprecated: Marked as deprecated in c1/connectorapi/baton/v1/baton.proto. type Task_CreateSyncDiffTask_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - // Open to suggestions here BaseSyncId string NewSyncId string Annotations []*anypb.Any @@ -5639,7 +5598,7 @@ var File_c1_connectorapi_baton_v1_baton_proto protoreflect.FileDescriptor const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\n" + - "$c1/connectorapi/baton/v1/baton.proto\x12\x18c1.connectorapi.baton.v1\x1a\x1fc1/connector/v2/connector.proto\x1a!c1/connector/v2/entitlement.proto\x1a\x1bc1/connector/v2/grant.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cc1/connector/v2/ticket.proto\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/rpc/status.proto\x1a\x17validate/validate.proto\"\xd51\n" + + "$c1/connectorapi/baton/v1/baton.proto\x12\x18c1.connectorapi.baton.v1\x1a\x1fc1/connector/v2/connector.proto\x1a!c1/connector/v2/entitlement.proto\x1a\x1bc1/connector/v2/grant.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cc1/connector/v2/ticket.proto\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/rpc/status.proto\x1a\x17validate/validate.proto\"\x921\n" + "\x04Task\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12=\n" + "\x06status\x18\x02 \x01(\x0e2%.c1.connectorapi.baton.v1.Task.StatusR\x06status\x12=\n" + @@ -5663,8 +5622,7 @@ const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\x13action_list_schemas\x18s \x01(\v24.c1.connectorapi.baton.v1.Task.ActionListSchemasTaskH\x00R\x11actionListSchemas\x12`\n" + "\x11action_get_schema\x18t \x01(\v22.c1.connectorapi.baton.v1.Task.ActionGetSchemaTaskH\x00R\x0factionGetSchema\x12V\n" + "\raction_invoke\x18u \x01(\v2/.c1.connectorapi.baton.v1.Task.ActionInvokeTaskH\x00R\factionInvoke\x12V\n" + - "\raction_status\x18v \x01(\v2/.c1.connectorapi.baton.v1.Task.ActionStatusTaskH\x00R\factionStatus\x12]\n" + - "\x10create_sync_diff\x18w \x01(\v21.c1.connectorapi.baton.v1.Task.CreateSyncDiffTaskH\x00R\x0ecreateSyncDiff\x12R\n" + + "\raction_status\x18v \x01(\v2/.c1.connectorapi.baton.v1.Task.ActionStatusTaskH\x00R\factionStatus\x12R\n" + "\rcompact_syncs\x18x \x01(\v2+.c1.connectorapi.baton.v1.Task.CompactSyncsH\x00R\fcompactSyncs\x12]\n" + "\x10list_event_feeds\x18y \x01(\v21.c1.connectorapi.baton.v1.Task.ListEventFeedsTaskH\x00R\x0elistEventFeeds\x12P\n" + "\vlist_events\x18z \x01(\v2-.c1.connectorapi.baton.v1.Task.ListEventsTaskH\x00R\n" + @@ -5756,12 +5714,12 @@ const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\x10ActionStatusTask\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x126\n" + - "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1a\x8e\x01\n" + + "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1a\x92\x01\n" + "\x12CreateSyncDiffTask\x12 \n" + "\fbase_sync_id\x18\x01 \x01(\tR\n" + "baseSyncId\x12\x1e\n" + "\vnew_sync_id\x18\x02 \x01(\tR\tnewSyncId\x126\n" + - "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1a\xf9\x01\n" + + "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations:\x02\x18\x01\x1a\xf9\x01\n" + "\fCompactSyncs\x12h\n" + "\x11compactable_syncs\x18\x01 \x03(\v2;.c1.connectorapi.baton.v1.Task.CompactSyncs.CompactableSyncR\x10compactableSyncs\x126\n" + "\vannotations\x18\x02 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1aG\n" + @@ -5774,7 +5732,7 @@ const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\x10STATUS_SCHEDULED\x10\x02\x12\x12\n" + "\x0eSTATUS_RUNNING\x10\x03\x12\x13\n" + "\x0fSTATUS_FINISHED\x10\x04B\v\n" + - "\ttask_type\"\xc9\a\n" + + "\ttask_typeJ\x04\bw\x10xR\x10create_sync_diff\"\xc9\a\n" + "\x18BatonServiceHelloRequest\x12#\n" + "\ahost_id\x18\x01 \x01(\tB\n" + "\xfaB\ar\x05\x10\x01\x18\x80\x02R\x06hostId\x122\n" + @@ -5968,104 +5926,103 @@ var file_c1_connectorapi_baton_v1_baton_proto_depIdxs = []int32{ 35, // 17: c1.connectorapi.baton.v1.Task.action_get_schema:type_name -> c1.connectorapi.baton.v1.Task.ActionGetSchemaTask 36, // 18: c1.connectorapi.baton.v1.Task.action_invoke:type_name -> c1.connectorapi.baton.v1.Task.ActionInvokeTask 37, // 19: c1.connectorapi.baton.v1.Task.action_status:type_name -> c1.connectorapi.baton.v1.Task.ActionStatusTask - 38, // 20: c1.connectorapi.baton.v1.Task.create_sync_diff:type_name -> c1.connectorapi.baton.v1.Task.CreateSyncDiffTask - 39, // 21: c1.connectorapi.baton.v1.Task.compact_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs - 21, // 22: c1.connectorapi.baton.v1.Task.list_event_feeds:type_name -> c1.connectorapi.baton.v1.Task.ListEventFeedsTask - 20, // 23: c1.connectorapi.baton.v1.Task.list_events:type_name -> c1.connectorapi.baton.v1.Task.ListEventsTask - 28, // 24: c1.connectorapi.baton.v1.Task.issue_credential:type_name -> c1.connectorapi.baton.v1.Task.IssueCredentialTask - 41, // 25: c1.connectorapi.baton.v1.BatonServiceHelloRequest.build_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.BuildInfo - 42, // 26: c1.connectorapi.baton.v1.BatonServiceHelloRequest.os_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.OSInfo - 48, // 27: c1.connectorapi.baton.v1.BatonServiceHelloRequest.connector_metadata:type_name -> c1.connector.v2.ConnectorMetadata - 49, // 28: c1.connectorapi.baton.v1.BatonServiceHelloRequest.annotations:type_name -> google.protobuf.Any - 49, // 29: c1.connectorapi.baton.v1.BatonServiceHelloResponse.annotations:type_name -> google.protobuf.Any - 49, // 30: c1.connectorapi.baton.v1.BatonServiceGetTasksRequest.annotations:type_name -> google.protobuf.Any - 1, // 31: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.tasks:type_name -> c1.connectorapi.baton.v1.Task - 50, // 32: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_poll:type_name -> google.protobuf.Duration - 50, // 33: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_heartbeat:type_name -> google.protobuf.Duration - 49, // 34: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.annotations:type_name -> google.protobuf.Any - 1, // 35: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.task:type_name -> c1.connectorapi.baton.v1.Task - 50, // 36: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_poll:type_name -> google.protobuf.Duration - 50, // 37: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_heartbeat:type_name -> google.protobuf.Duration - 49, // 38: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.annotations:type_name -> google.protobuf.Any - 49, // 39: c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest.annotations:type_name -> google.protobuf.Any - 50, // 40: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.next_heartbeat:type_name -> google.protobuf.Duration - 49, // 41: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.annotations:type_name -> google.protobuf.Any - 43, // 42: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.metadata:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata - 44, // 43: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.data:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadData - 45, // 44: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.eof:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF - 49, // 45: c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse.annotations:type_name -> google.protobuf.Any - 51, // 46: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.status:type_name -> google.rpc.Status - 46, // 47: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.error:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error - 47, // 48: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.success:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success - 49, // 49: c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse.annotations:type_name -> google.protobuf.Any - 49, // 50: c1.connectorapi.baton.v1.Task.NoneTask.annotations:type_name -> google.protobuf.Any - 49, // 51: c1.connectorapi.baton.v1.Task.HelloTask.annotations:type_name -> google.protobuf.Any - 49, // 52: c1.connectorapi.baton.v1.Task.SyncFullTask.annotations:type_name -> google.protobuf.Any - 52, // 53: c1.connectorapi.baton.v1.Task.SyncFullTask.targeted_sync_resources:type_name -> c1.connector.v2.Resource - 49, // 54: c1.connectorapi.baton.v1.Task.EventFeedTask.annotations:type_name -> google.protobuf.Any - 53, // 55: c1.connectorapi.baton.v1.Task.EventFeedTask.start_at:type_name -> google.protobuf.Timestamp - 49, // 56: c1.connectorapi.baton.v1.Task.ListEventsTask.annotations:type_name -> google.protobuf.Any - 53, // 57: c1.connectorapi.baton.v1.Task.ListEventsTask.start_at:type_name -> google.protobuf.Timestamp - 49, // 58: c1.connectorapi.baton.v1.Task.ListEventFeedsTask.annotations:type_name -> google.protobuf.Any - 54, // 59: c1.connectorapi.baton.v1.Task.GrantTask.entitlement:type_name -> c1.connector.v2.Entitlement - 52, // 60: c1.connectorapi.baton.v1.Task.GrantTask.principal:type_name -> c1.connector.v2.Resource - 49, // 61: c1.connectorapi.baton.v1.Task.GrantTask.annotations:type_name -> google.protobuf.Any - 50, // 62: c1.connectorapi.baton.v1.Task.GrantTask.duration:type_name -> google.protobuf.Duration - 55, // 63: c1.connectorapi.baton.v1.Task.RevokeTask.grant:type_name -> c1.connector.v2.Grant - 49, // 64: c1.connectorapi.baton.v1.Task.RevokeTask.annotations:type_name -> google.protobuf.Any - 56, // 65: c1.connectorapi.baton.v1.Task.CreateAccountTask.account_info:type_name -> c1.connector.v2.AccountInfo - 57, // 66: c1.connectorapi.baton.v1.Task.CreateAccountTask.credential_options:type_name -> c1.connector.v2.CredentialOptions - 58, // 67: c1.connectorapi.baton.v1.Task.CreateAccountTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig - 52, // 68: c1.connectorapi.baton.v1.Task.CreateResourceTask.resource:type_name -> c1.connector.v2.Resource - 59, // 69: c1.connectorapi.baton.v1.Task.DeleteResourceTask.resource_id:type_name -> c1.connector.v2.ResourceId - 59, // 70: c1.connectorapi.baton.v1.Task.DeleteResourceTask.parent_resource_id:type_name -> c1.connector.v2.ResourceId - 59, // 71: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.resource_id:type_name -> c1.connector.v2.ResourceId - 57, // 72: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.credential_options:type_name -> c1.connector.v2.CredentialOptions - 58, // 73: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig - 59, // 74: c1.connectorapi.baton.v1.Task.IssueCredentialTask.identity_id:type_name -> c1.connector.v2.ResourceId - 60, // 75: c1.connectorapi.baton.v1.Task.IssueCredentialTask.credential_options:type_name -> c1.connector.v2.CredentialIssueOptions - 58, // 76: c1.connectorapi.baton.v1.Task.IssueCredentialTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig - 53, // 77: c1.connectorapi.baton.v1.Task.IssueCredentialTask.expires_at:type_name -> google.protobuf.Timestamp - 61, // 78: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_request:type_name -> c1.connector.v2.TicketRequest - 62, // 79: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_schema:type_name -> c1.connector.v2.TicketSchema - 49, // 80: c1.connectorapi.baton.v1.Task.CreateTicketTask.annotations:type_name -> google.protobuf.Any - 29, // 81: c1.connectorapi.baton.v1.Task.BulkCreateTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.CreateTicketTask - 33, // 82: c1.connectorapi.baton.v1.Task.BulkGetTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.GetTicketTask - 49, // 83: c1.connectorapi.baton.v1.Task.ListTicketSchemasTask.annotations:type_name -> google.protobuf.Any - 49, // 84: c1.connectorapi.baton.v1.Task.GetTicketTask.annotations:type_name -> google.protobuf.Any - 49, // 85: c1.connectorapi.baton.v1.Task.ActionListSchemasTask.annotations:type_name -> google.protobuf.Any - 49, // 86: c1.connectorapi.baton.v1.Task.ActionGetSchemaTask.annotations:type_name -> google.protobuf.Any - 63, // 87: c1.connectorapi.baton.v1.Task.ActionInvokeTask.args:type_name -> google.protobuf.Struct - 49, // 88: c1.connectorapi.baton.v1.Task.ActionInvokeTask.annotations:type_name -> google.protobuf.Any - 49, // 89: c1.connectorapi.baton.v1.Task.ActionStatusTask.annotations:type_name -> google.protobuf.Any - 49, // 90: c1.connectorapi.baton.v1.Task.CreateSyncDiffTask.annotations:type_name -> google.protobuf.Any - 40, // 91: c1.connectorapi.baton.v1.Task.CompactSyncs.compactable_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs.CompactableSync - 49, // 92: c1.connectorapi.baton.v1.Task.CompactSyncs.annotations:type_name -> google.protobuf.Any - 49, // 93: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata.annotations:type_name -> google.protobuf.Any - 49, // 94: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF.annotations:type_name -> google.protobuf.Any - 49, // 95: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.annotations:type_name -> google.protobuf.Any - 49, // 96: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.response:type_name -> google.protobuf.Any - 49, // 97: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.annotations:type_name -> google.protobuf.Any - 49, // 98: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.response:type_name -> google.protobuf.Any - 2, // 99: c1.connectorapi.baton.v1.BatonService.Hello:input_type -> c1.connectorapi.baton.v1.BatonServiceHelloRequest - 4, // 100: c1.connectorapi.baton.v1.BatonService.GetTask:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskRequest - 5, // 101: c1.connectorapi.baton.v1.BatonService.GetTasks:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksRequest - 8, // 102: c1.connectorapi.baton.v1.BatonService.Heartbeat:input_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest - 12, // 103: c1.connectorapi.baton.v1.BatonService.FinishTask:input_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest - 10, // 104: c1.connectorapi.baton.v1.BatonService.UploadAsset:input_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest - 14, // 105: c1.connectorapi.baton.v1.BatonService.StartDebugging:input_type -> c1.connectorapi.baton.v1.StartDebuggingRequest - 3, // 106: c1.connectorapi.baton.v1.BatonService.Hello:output_type -> c1.connectorapi.baton.v1.BatonServiceHelloResponse - 7, // 107: c1.connectorapi.baton.v1.BatonService.GetTask:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskResponse - 6, // 108: c1.connectorapi.baton.v1.BatonService.GetTasks:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksResponse - 9, // 109: c1.connectorapi.baton.v1.BatonService.Heartbeat:output_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse - 13, // 110: c1.connectorapi.baton.v1.BatonService.FinishTask:output_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse - 11, // 111: c1.connectorapi.baton.v1.BatonService.UploadAsset:output_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse - 15, // 112: c1.connectorapi.baton.v1.BatonService.StartDebugging:output_type -> c1.connectorapi.baton.v1.StartDebuggingResponse - 106, // [106:113] is the sub-list for method output_type - 99, // [99:106] is the sub-list for method input_type - 99, // [99:99] is the sub-list for extension type_name - 99, // [99:99] is the sub-list for extension extendee - 0, // [0:99] is the sub-list for field type_name + 39, // 20: c1.connectorapi.baton.v1.Task.compact_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs + 21, // 21: c1.connectorapi.baton.v1.Task.list_event_feeds:type_name -> c1.connectorapi.baton.v1.Task.ListEventFeedsTask + 20, // 22: c1.connectorapi.baton.v1.Task.list_events:type_name -> c1.connectorapi.baton.v1.Task.ListEventsTask + 28, // 23: c1.connectorapi.baton.v1.Task.issue_credential:type_name -> c1.connectorapi.baton.v1.Task.IssueCredentialTask + 41, // 24: c1.connectorapi.baton.v1.BatonServiceHelloRequest.build_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.BuildInfo + 42, // 25: c1.connectorapi.baton.v1.BatonServiceHelloRequest.os_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.OSInfo + 48, // 26: c1.connectorapi.baton.v1.BatonServiceHelloRequest.connector_metadata:type_name -> c1.connector.v2.ConnectorMetadata + 49, // 27: c1.connectorapi.baton.v1.BatonServiceHelloRequest.annotations:type_name -> google.protobuf.Any + 49, // 28: c1.connectorapi.baton.v1.BatonServiceHelloResponse.annotations:type_name -> google.protobuf.Any + 49, // 29: c1.connectorapi.baton.v1.BatonServiceGetTasksRequest.annotations:type_name -> google.protobuf.Any + 1, // 30: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.tasks:type_name -> c1.connectorapi.baton.v1.Task + 50, // 31: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_poll:type_name -> google.protobuf.Duration + 50, // 32: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_heartbeat:type_name -> google.protobuf.Duration + 49, // 33: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.annotations:type_name -> google.protobuf.Any + 1, // 34: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.task:type_name -> c1.connectorapi.baton.v1.Task + 50, // 35: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_poll:type_name -> google.protobuf.Duration + 50, // 36: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_heartbeat:type_name -> google.protobuf.Duration + 49, // 37: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.annotations:type_name -> google.protobuf.Any + 49, // 38: c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest.annotations:type_name -> google.protobuf.Any + 50, // 39: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.next_heartbeat:type_name -> google.protobuf.Duration + 49, // 40: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.annotations:type_name -> google.protobuf.Any + 43, // 41: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.metadata:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata + 44, // 42: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.data:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadData + 45, // 43: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.eof:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF + 49, // 44: c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse.annotations:type_name -> google.protobuf.Any + 51, // 45: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.status:type_name -> google.rpc.Status + 46, // 46: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.error:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error + 47, // 47: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.success:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success + 49, // 48: c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse.annotations:type_name -> google.protobuf.Any + 49, // 49: c1.connectorapi.baton.v1.Task.NoneTask.annotations:type_name -> google.protobuf.Any + 49, // 50: c1.connectorapi.baton.v1.Task.HelloTask.annotations:type_name -> google.protobuf.Any + 49, // 51: c1.connectorapi.baton.v1.Task.SyncFullTask.annotations:type_name -> google.protobuf.Any + 52, // 52: c1.connectorapi.baton.v1.Task.SyncFullTask.targeted_sync_resources:type_name -> c1.connector.v2.Resource + 49, // 53: c1.connectorapi.baton.v1.Task.EventFeedTask.annotations:type_name -> google.protobuf.Any + 53, // 54: c1.connectorapi.baton.v1.Task.EventFeedTask.start_at:type_name -> google.protobuf.Timestamp + 49, // 55: c1.connectorapi.baton.v1.Task.ListEventsTask.annotations:type_name -> google.protobuf.Any + 53, // 56: c1.connectorapi.baton.v1.Task.ListEventsTask.start_at:type_name -> google.protobuf.Timestamp + 49, // 57: c1.connectorapi.baton.v1.Task.ListEventFeedsTask.annotations:type_name -> google.protobuf.Any + 54, // 58: c1.connectorapi.baton.v1.Task.GrantTask.entitlement:type_name -> c1.connector.v2.Entitlement + 52, // 59: c1.connectorapi.baton.v1.Task.GrantTask.principal:type_name -> c1.connector.v2.Resource + 49, // 60: c1.connectorapi.baton.v1.Task.GrantTask.annotations:type_name -> google.protobuf.Any + 50, // 61: c1.connectorapi.baton.v1.Task.GrantTask.duration:type_name -> google.protobuf.Duration + 55, // 62: c1.connectorapi.baton.v1.Task.RevokeTask.grant:type_name -> c1.connector.v2.Grant + 49, // 63: c1.connectorapi.baton.v1.Task.RevokeTask.annotations:type_name -> google.protobuf.Any + 56, // 64: c1.connectorapi.baton.v1.Task.CreateAccountTask.account_info:type_name -> c1.connector.v2.AccountInfo + 57, // 65: c1.connectorapi.baton.v1.Task.CreateAccountTask.credential_options:type_name -> c1.connector.v2.CredentialOptions + 58, // 66: c1.connectorapi.baton.v1.Task.CreateAccountTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig + 52, // 67: c1.connectorapi.baton.v1.Task.CreateResourceTask.resource:type_name -> c1.connector.v2.Resource + 59, // 68: c1.connectorapi.baton.v1.Task.DeleteResourceTask.resource_id:type_name -> c1.connector.v2.ResourceId + 59, // 69: c1.connectorapi.baton.v1.Task.DeleteResourceTask.parent_resource_id:type_name -> c1.connector.v2.ResourceId + 59, // 70: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.resource_id:type_name -> c1.connector.v2.ResourceId + 57, // 71: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.credential_options:type_name -> c1.connector.v2.CredentialOptions + 58, // 72: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig + 59, // 73: c1.connectorapi.baton.v1.Task.IssueCredentialTask.identity_id:type_name -> c1.connector.v2.ResourceId + 60, // 74: c1.connectorapi.baton.v1.Task.IssueCredentialTask.credential_options:type_name -> c1.connector.v2.CredentialIssueOptions + 58, // 75: c1.connectorapi.baton.v1.Task.IssueCredentialTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig + 53, // 76: c1.connectorapi.baton.v1.Task.IssueCredentialTask.expires_at:type_name -> google.protobuf.Timestamp + 61, // 77: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_request:type_name -> c1.connector.v2.TicketRequest + 62, // 78: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_schema:type_name -> c1.connector.v2.TicketSchema + 49, // 79: c1.connectorapi.baton.v1.Task.CreateTicketTask.annotations:type_name -> google.protobuf.Any + 29, // 80: c1.connectorapi.baton.v1.Task.BulkCreateTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.CreateTicketTask + 33, // 81: c1.connectorapi.baton.v1.Task.BulkGetTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.GetTicketTask + 49, // 82: c1.connectorapi.baton.v1.Task.ListTicketSchemasTask.annotations:type_name -> google.protobuf.Any + 49, // 83: c1.connectorapi.baton.v1.Task.GetTicketTask.annotations:type_name -> google.protobuf.Any + 49, // 84: c1.connectorapi.baton.v1.Task.ActionListSchemasTask.annotations:type_name -> google.protobuf.Any + 49, // 85: c1.connectorapi.baton.v1.Task.ActionGetSchemaTask.annotations:type_name -> google.protobuf.Any + 63, // 86: c1.connectorapi.baton.v1.Task.ActionInvokeTask.args:type_name -> google.protobuf.Struct + 49, // 87: c1.connectorapi.baton.v1.Task.ActionInvokeTask.annotations:type_name -> google.protobuf.Any + 49, // 88: c1.connectorapi.baton.v1.Task.ActionStatusTask.annotations:type_name -> google.protobuf.Any + 49, // 89: c1.connectorapi.baton.v1.Task.CreateSyncDiffTask.annotations:type_name -> google.protobuf.Any + 40, // 90: c1.connectorapi.baton.v1.Task.CompactSyncs.compactable_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs.CompactableSync + 49, // 91: c1.connectorapi.baton.v1.Task.CompactSyncs.annotations:type_name -> google.protobuf.Any + 49, // 92: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata.annotations:type_name -> google.protobuf.Any + 49, // 93: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF.annotations:type_name -> google.protobuf.Any + 49, // 94: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.annotations:type_name -> google.protobuf.Any + 49, // 95: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.response:type_name -> google.protobuf.Any + 49, // 96: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.annotations:type_name -> google.protobuf.Any + 49, // 97: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.response:type_name -> google.protobuf.Any + 2, // 98: c1.connectorapi.baton.v1.BatonService.Hello:input_type -> c1.connectorapi.baton.v1.BatonServiceHelloRequest + 4, // 99: c1.connectorapi.baton.v1.BatonService.GetTask:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskRequest + 5, // 100: c1.connectorapi.baton.v1.BatonService.GetTasks:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksRequest + 8, // 101: c1.connectorapi.baton.v1.BatonService.Heartbeat:input_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest + 12, // 102: c1.connectorapi.baton.v1.BatonService.FinishTask:input_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest + 10, // 103: c1.connectorapi.baton.v1.BatonService.UploadAsset:input_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest + 14, // 104: c1.connectorapi.baton.v1.BatonService.StartDebugging:input_type -> c1.connectorapi.baton.v1.StartDebuggingRequest + 3, // 105: c1.connectorapi.baton.v1.BatonService.Hello:output_type -> c1.connectorapi.baton.v1.BatonServiceHelloResponse + 7, // 106: c1.connectorapi.baton.v1.BatonService.GetTask:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskResponse + 6, // 107: c1.connectorapi.baton.v1.BatonService.GetTasks:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksResponse + 9, // 108: c1.connectorapi.baton.v1.BatonService.Heartbeat:output_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse + 13, // 109: c1.connectorapi.baton.v1.BatonService.FinishTask:output_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse + 11, // 110: c1.connectorapi.baton.v1.BatonService.UploadAsset:output_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse + 15, // 111: c1.connectorapi.baton.v1.BatonService.StartDebugging:output_type -> c1.connectorapi.baton.v1.StartDebuggingResponse + 105, // [105:112] is the sub-list for method output_type + 98, // [98:105] is the sub-list for method input_type + 98, // [98:98] is the sub-list for extension type_name + 98, // [98:98] is the sub-list for extension extendee + 0, // [0:98] is the sub-list for field type_name } func init() { file_c1_connectorapi_baton_v1_baton_proto_init() } @@ -6093,7 +6050,6 @@ func file_c1_connectorapi_baton_v1_baton_proto_init() { (*Task_ActionGetSchema)(nil), (*Task_ActionInvoke)(nil), (*Task_ActionStatus)(nil), - (*Task_CreateSyncDiff)(nil), (*Task_CompactSyncs_)(nil), (*Task_ListEventFeeds)(nil), (*Task_ListEvents)(nil), diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.validate.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.validate.go index 559b0b0d..a333caa7 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.validate.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton.pb.validate.go @@ -842,47 +842,6 @@ func (m *Task) validate(all bool) error { } } - case *Task_CreateSyncDiff: - if v == nil { - err := TaskValidationError{ - field: "TaskType", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - - if all { - switch v := interface{}(m.GetCreateSyncDiff()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, TaskValidationError{ - field: "CreateSyncDiff", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, TaskValidationError{ - field: "CreateSyncDiff", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetCreateSyncDiff()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return TaskValidationError{ - field: "CreateSyncDiff", - reason: "embedded message failed validation", - cause: err, - } - } - } - case *Task_CompactSyncs_: if v == nil { err := TaskValidationError{ diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton_protoopaque.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton_protoopaque.pb.go index 833a47a4..6a985011 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton_protoopaque.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1/baton_protoopaque.pb.go @@ -299,15 +299,6 @@ func (x *Task) GetActionStatus() *Task_ActionStatusTask { return nil } -func (x *Task) GetCreateSyncDiff() *Task_CreateSyncDiffTask { - if x != nil { - if x, ok := x.xxx_hidden_TaskType.(*task_CreateSyncDiff); ok { - return x.CreateSyncDiff - } - } - return nil -} - func (x *Task) GetCompactSyncs() *Task_CompactSyncs { if x != nil { if x, ok := x.xxx_hidden_TaskType.(*task_CompactSyncs_); ok { @@ -511,14 +502,6 @@ func (x *Task) SetActionStatus(v *Task_ActionStatusTask) { x.xxx_hidden_TaskType = &task_ActionStatus{v} } -func (x *Task) SetCreateSyncDiff(v *Task_CreateSyncDiffTask) { - if v == nil { - x.xxx_hidden_TaskType = nil - return - } - x.xxx_hidden_TaskType = &task_CreateSyncDiff{v} -} - func (x *Task) SetCompactSyncs(v *Task_CompactSyncs) { if v == nil { x.xxx_hidden_TaskType = nil @@ -714,14 +697,6 @@ func (x *Task) HasActionStatus() bool { return ok } -func (x *Task) HasCreateSyncDiff() bool { - if x == nil { - return false - } - _, ok := x.xxx_hidden_TaskType.(*task_CreateSyncDiff) - return ok -} - func (x *Task) HasCompactSyncs() bool { if x == nil { return false @@ -872,12 +847,6 @@ func (x *Task) ClearActionStatus() { } } -func (x *Task) ClearCreateSyncDiff() { - if _, ok := x.xxx_hidden_TaskType.(*task_CreateSyncDiff); ok { - x.xxx_hidden_TaskType = nil - } -} - func (x *Task) ClearCompactSyncs() { if _, ok := x.xxx_hidden_TaskType.(*task_CompactSyncs_); ok { x.xxx_hidden_TaskType = nil @@ -922,7 +891,6 @@ const Task_ActionListSchemas_case case_Task_TaskType = 115 const Task_ActionGetSchema_case case_Task_TaskType = 116 const Task_ActionInvoke_case case_Task_TaskType = 117 const Task_ActionStatus_case case_Task_TaskType = 118 -const Task_CreateSyncDiff_case case_Task_TaskType = 119 const Task_CompactSyncs_case case_Task_TaskType = 120 const Task_ListEventFeeds_case case_Task_TaskType = 121 const Task_ListEvents_case case_Task_TaskType = 122 @@ -971,8 +939,6 @@ func (x *Task) WhichTaskType() case_Task_TaskType { return Task_ActionInvoke_case case *task_ActionStatus: return Task_ActionStatus_case - case *task_CreateSyncDiff: - return Task_CreateSyncDiff_case case *task_CompactSyncs_: return Task_CompactSyncs_case case *task_ListEventFeeds: @@ -1011,7 +977,6 @@ type Task_builder struct { ActionGetSchema *Task_ActionGetSchemaTask ActionInvoke *Task_ActionInvokeTask ActionStatus *Task_ActionStatusTask - CreateSyncDiff *Task_CreateSyncDiffTask CompactSyncs *Task_CompactSyncs ListEventFeeds *Task_ListEventFeedsTask ListEvents *Task_ListEventsTask @@ -1083,9 +1048,6 @@ func (b0 Task_builder) Build() *Task { if b.ActionStatus != nil { x.xxx_hidden_TaskType = &task_ActionStatus{b.ActionStatus} } - if b.CreateSyncDiff != nil { - x.xxx_hidden_TaskType = &task_CreateSyncDiff{b.CreateSyncDiff} - } if b.CompactSyncs != nil { x.xxx_hidden_TaskType = &task_CompactSyncs_{b.CompactSyncs} } @@ -1192,10 +1154,6 @@ type task_ActionStatus struct { ActionStatus *Task_ActionStatusTask `protobuf:"bytes,118,opt,name=action_status,json=actionStatus,proto3,oneof"` } -type task_CreateSyncDiff struct { - CreateSyncDiff *Task_CreateSyncDiffTask `protobuf:"bytes,119,opt,name=create_sync_diff,json=createSyncDiff,proto3,oneof"` -} - type task_CompactSyncs_ struct { CompactSyncs *Task_CompactSyncs `protobuf:"bytes,120,opt,name=compact_syncs,json=compactSyncs,proto3,oneof"` } @@ -1250,8 +1208,6 @@ func (*task_ActionInvoke) isTask_TaskType() {} func (*task_ActionStatus) isTask_TaskType() {} -func (*task_CreateSyncDiff) isTask_TaskType() {} - func (*task_CompactSyncs_) isTask_TaskType() {} func (*task_ListEventFeeds) isTask_TaskType() {} @@ -3022,7 +2978,7 @@ type Task_SyncFullTask_builder struct { SyncResourceTypeIds []string // If true, skip syncing grants. Resources and entitlements will still be synced. SkipGrants bool - // Storage engine to use for the sync. If empty, the default engine will be used (currently SQLite). + // Storage engine to use for the sync. If empty, the default engine will be used (currently Pebble for new c1z files; existing files keep their on-disk format). StorageEngine string } @@ -4760,6 +4716,11 @@ func (b0 Task_ActionStatusTask_builder) Build() *Task_ActionStatusTask { return m0 } +// Deprecated: diff-sync support was removed from the SDK. The message +// is retained only to satisfy breaking-change detection; nothing +// produces or consumes it. +// +// Deprecated: Marked as deprecated in c1/connectorapi/baton/v1/baton.proto. type Task_CreateSyncDiffTask struct { state protoimpl.MessageState `protogen:"opaque.v1"` xxx_hidden_BaseSyncId string `protobuf:"bytes,1,opt,name=base_sync_id,json=baseSyncId,proto3"` @@ -4829,10 +4790,10 @@ func (x *Task_CreateSyncDiffTask) SetAnnotations(v []*anypb.Any) { x.xxx_hidden_Annotations = &v } +// Deprecated: Marked as deprecated in c1/connectorapi/baton/v1/baton.proto. type Task_CreateSyncDiffTask_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - // Open to suggestions here BaseSyncId string NewSyncId string Annotations []*anypb.Any @@ -5646,7 +5607,7 @@ var File_c1_connectorapi_baton_v1_baton_proto protoreflect.FileDescriptor const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\n" + - "$c1/connectorapi/baton/v1/baton.proto\x12\x18c1.connectorapi.baton.v1\x1a\x1fc1/connector/v2/connector.proto\x1a!c1/connector/v2/entitlement.proto\x1a\x1bc1/connector/v2/grant.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cc1/connector/v2/ticket.proto\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/rpc/status.proto\x1a\x17validate/validate.proto\"\xd51\n" + + "$c1/connectorapi/baton/v1/baton.proto\x12\x18c1.connectorapi.baton.v1\x1a\x1fc1/connector/v2/connector.proto\x1a!c1/connector/v2/entitlement.proto\x1a\x1bc1/connector/v2/grant.proto\x1a\x1ec1/connector/v2/resource.proto\x1a\x1cc1/connector/v2/ticket.proto\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/rpc/status.proto\x1a\x17validate/validate.proto\"\x921\n" + "\x04Task\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12=\n" + "\x06status\x18\x02 \x01(\x0e2%.c1.connectorapi.baton.v1.Task.StatusR\x06status\x12=\n" + @@ -5670,8 +5631,7 @@ const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\x13action_list_schemas\x18s \x01(\v24.c1.connectorapi.baton.v1.Task.ActionListSchemasTaskH\x00R\x11actionListSchemas\x12`\n" + "\x11action_get_schema\x18t \x01(\v22.c1.connectorapi.baton.v1.Task.ActionGetSchemaTaskH\x00R\x0factionGetSchema\x12V\n" + "\raction_invoke\x18u \x01(\v2/.c1.connectorapi.baton.v1.Task.ActionInvokeTaskH\x00R\factionInvoke\x12V\n" + - "\raction_status\x18v \x01(\v2/.c1.connectorapi.baton.v1.Task.ActionStatusTaskH\x00R\factionStatus\x12]\n" + - "\x10create_sync_diff\x18w \x01(\v21.c1.connectorapi.baton.v1.Task.CreateSyncDiffTaskH\x00R\x0ecreateSyncDiff\x12R\n" + + "\raction_status\x18v \x01(\v2/.c1.connectorapi.baton.v1.Task.ActionStatusTaskH\x00R\factionStatus\x12R\n" + "\rcompact_syncs\x18x \x01(\v2+.c1.connectorapi.baton.v1.Task.CompactSyncsH\x00R\fcompactSyncs\x12]\n" + "\x10list_event_feeds\x18y \x01(\v21.c1.connectorapi.baton.v1.Task.ListEventFeedsTaskH\x00R\x0elistEventFeeds\x12P\n" + "\vlist_events\x18z \x01(\v2-.c1.connectorapi.baton.v1.Task.ListEventsTaskH\x00R\n" + @@ -5763,12 +5723,12 @@ const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\x10ActionStatusTask\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x126\n" + - "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1a\x8e\x01\n" + + "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1a\x92\x01\n" + "\x12CreateSyncDiffTask\x12 \n" + "\fbase_sync_id\x18\x01 \x01(\tR\n" + "baseSyncId\x12\x1e\n" + "\vnew_sync_id\x18\x02 \x01(\tR\tnewSyncId\x126\n" + - "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1a\xf9\x01\n" + + "\vannotations\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\vannotations:\x02\x18\x01\x1a\xf9\x01\n" + "\fCompactSyncs\x12h\n" + "\x11compactable_syncs\x18\x01 \x03(\v2;.c1.connectorapi.baton.v1.Task.CompactSyncs.CompactableSyncR\x10compactableSyncs\x126\n" + "\vannotations\x18\x02 \x03(\v2\x14.google.protobuf.AnyR\vannotations\x1aG\n" + @@ -5781,7 +5741,7 @@ const file_c1_connectorapi_baton_v1_baton_proto_rawDesc = "" + "\x10STATUS_SCHEDULED\x10\x02\x12\x12\n" + "\x0eSTATUS_RUNNING\x10\x03\x12\x13\n" + "\x0fSTATUS_FINISHED\x10\x04B\v\n" + - "\ttask_type\"\xc9\a\n" + + "\ttask_typeJ\x04\bw\x10xR\x10create_sync_diff\"\xc9\a\n" + "\x18BatonServiceHelloRequest\x12#\n" + "\ahost_id\x18\x01 \x01(\tB\n" + "\xfaB\ar\x05\x10\x01\x18\x80\x02R\x06hostId\x122\n" + @@ -5975,104 +5935,103 @@ var file_c1_connectorapi_baton_v1_baton_proto_depIdxs = []int32{ 35, // 17: c1.connectorapi.baton.v1.Task.action_get_schema:type_name -> c1.connectorapi.baton.v1.Task.ActionGetSchemaTask 36, // 18: c1.connectorapi.baton.v1.Task.action_invoke:type_name -> c1.connectorapi.baton.v1.Task.ActionInvokeTask 37, // 19: c1.connectorapi.baton.v1.Task.action_status:type_name -> c1.connectorapi.baton.v1.Task.ActionStatusTask - 38, // 20: c1.connectorapi.baton.v1.Task.create_sync_diff:type_name -> c1.connectorapi.baton.v1.Task.CreateSyncDiffTask - 39, // 21: c1.connectorapi.baton.v1.Task.compact_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs - 21, // 22: c1.connectorapi.baton.v1.Task.list_event_feeds:type_name -> c1.connectorapi.baton.v1.Task.ListEventFeedsTask - 20, // 23: c1.connectorapi.baton.v1.Task.list_events:type_name -> c1.connectorapi.baton.v1.Task.ListEventsTask - 28, // 24: c1.connectorapi.baton.v1.Task.issue_credential:type_name -> c1.connectorapi.baton.v1.Task.IssueCredentialTask - 41, // 25: c1.connectorapi.baton.v1.BatonServiceHelloRequest.build_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.BuildInfo - 42, // 26: c1.connectorapi.baton.v1.BatonServiceHelloRequest.os_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.OSInfo - 48, // 27: c1.connectorapi.baton.v1.BatonServiceHelloRequest.connector_metadata:type_name -> c1.connector.v2.ConnectorMetadata - 49, // 28: c1.connectorapi.baton.v1.BatonServiceHelloRequest.annotations:type_name -> google.protobuf.Any - 49, // 29: c1.connectorapi.baton.v1.BatonServiceHelloResponse.annotations:type_name -> google.protobuf.Any - 49, // 30: c1.connectorapi.baton.v1.BatonServiceGetTasksRequest.annotations:type_name -> google.protobuf.Any - 1, // 31: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.tasks:type_name -> c1.connectorapi.baton.v1.Task - 50, // 32: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_poll:type_name -> google.protobuf.Duration - 50, // 33: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_heartbeat:type_name -> google.protobuf.Duration - 49, // 34: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.annotations:type_name -> google.protobuf.Any - 1, // 35: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.task:type_name -> c1.connectorapi.baton.v1.Task - 50, // 36: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_poll:type_name -> google.protobuf.Duration - 50, // 37: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_heartbeat:type_name -> google.protobuf.Duration - 49, // 38: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.annotations:type_name -> google.protobuf.Any - 49, // 39: c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest.annotations:type_name -> google.protobuf.Any - 50, // 40: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.next_heartbeat:type_name -> google.protobuf.Duration - 49, // 41: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.annotations:type_name -> google.protobuf.Any - 43, // 42: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.metadata:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata - 44, // 43: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.data:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadData - 45, // 44: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.eof:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF - 49, // 45: c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse.annotations:type_name -> google.protobuf.Any - 51, // 46: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.status:type_name -> google.rpc.Status - 46, // 47: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.error:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error - 47, // 48: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.success:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success - 49, // 49: c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse.annotations:type_name -> google.protobuf.Any - 49, // 50: c1.connectorapi.baton.v1.Task.NoneTask.annotations:type_name -> google.protobuf.Any - 49, // 51: c1.connectorapi.baton.v1.Task.HelloTask.annotations:type_name -> google.protobuf.Any - 49, // 52: c1.connectorapi.baton.v1.Task.SyncFullTask.annotations:type_name -> google.protobuf.Any - 52, // 53: c1.connectorapi.baton.v1.Task.SyncFullTask.targeted_sync_resources:type_name -> c1.connector.v2.Resource - 49, // 54: c1.connectorapi.baton.v1.Task.EventFeedTask.annotations:type_name -> google.protobuf.Any - 53, // 55: c1.connectorapi.baton.v1.Task.EventFeedTask.start_at:type_name -> google.protobuf.Timestamp - 49, // 56: c1.connectorapi.baton.v1.Task.ListEventsTask.annotations:type_name -> google.protobuf.Any - 53, // 57: c1.connectorapi.baton.v1.Task.ListEventsTask.start_at:type_name -> google.protobuf.Timestamp - 49, // 58: c1.connectorapi.baton.v1.Task.ListEventFeedsTask.annotations:type_name -> google.protobuf.Any - 54, // 59: c1.connectorapi.baton.v1.Task.GrantTask.entitlement:type_name -> c1.connector.v2.Entitlement - 52, // 60: c1.connectorapi.baton.v1.Task.GrantTask.principal:type_name -> c1.connector.v2.Resource - 49, // 61: c1.connectorapi.baton.v1.Task.GrantTask.annotations:type_name -> google.protobuf.Any - 50, // 62: c1.connectorapi.baton.v1.Task.GrantTask.duration:type_name -> google.protobuf.Duration - 55, // 63: c1.connectorapi.baton.v1.Task.RevokeTask.grant:type_name -> c1.connector.v2.Grant - 49, // 64: c1.connectorapi.baton.v1.Task.RevokeTask.annotations:type_name -> google.protobuf.Any - 56, // 65: c1.connectorapi.baton.v1.Task.CreateAccountTask.account_info:type_name -> c1.connector.v2.AccountInfo - 57, // 66: c1.connectorapi.baton.v1.Task.CreateAccountTask.credential_options:type_name -> c1.connector.v2.CredentialOptions - 58, // 67: c1.connectorapi.baton.v1.Task.CreateAccountTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig - 52, // 68: c1.connectorapi.baton.v1.Task.CreateResourceTask.resource:type_name -> c1.connector.v2.Resource - 59, // 69: c1.connectorapi.baton.v1.Task.DeleteResourceTask.resource_id:type_name -> c1.connector.v2.ResourceId - 59, // 70: c1.connectorapi.baton.v1.Task.DeleteResourceTask.parent_resource_id:type_name -> c1.connector.v2.ResourceId - 59, // 71: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.resource_id:type_name -> c1.connector.v2.ResourceId - 57, // 72: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.credential_options:type_name -> c1.connector.v2.CredentialOptions - 58, // 73: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig - 59, // 74: c1.connectorapi.baton.v1.Task.IssueCredentialTask.identity_id:type_name -> c1.connector.v2.ResourceId - 60, // 75: c1.connectorapi.baton.v1.Task.IssueCredentialTask.credential_options:type_name -> c1.connector.v2.CredentialIssueOptions - 58, // 76: c1.connectorapi.baton.v1.Task.IssueCredentialTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig - 53, // 77: c1.connectorapi.baton.v1.Task.IssueCredentialTask.expires_at:type_name -> google.protobuf.Timestamp - 61, // 78: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_request:type_name -> c1.connector.v2.TicketRequest - 62, // 79: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_schema:type_name -> c1.connector.v2.TicketSchema - 49, // 80: c1.connectorapi.baton.v1.Task.CreateTicketTask.annotations:type_name -> google.protobuf.Any - 29, // 81: c1.connectorapi.baton.v1.Task.BulkCreateTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.CreateTicketTask - 33, // 82: c1.connectorapi.baton.v1.Task.BulkGetTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.GetTicketTask - 49, // 83: c1.connectorapi.baton.v1.Task.ListTicketSchemasTask.annotations:type_name -> google.protobuf.Any - 49, // 84: c1.connectorapi.baton.v1.Task.GetTicketTask.annotations:type_name -> google.protobuf.Any - 49, // 85: c1.connectorapi.baton.v1.Task.ActionListSchemasTask.annotations:type_name -> google.protobuf.Any - 49, // 86: c1.connectorapi.baton.v1.Task.ActionGetSchemaTask.annotations:type_name -> google.protobuf.Any - 63, // 87: c1.connectorapi.baton.v1.Task.ActionInvokeTask.args:type_name -> google.protobuf.Struct - 49, // 88: c1.connectorapi.baton.v1.Task.ActionInvokeTask.annotations:type_name -> google.protobuf.Any - 49, // 89: c1.connectorapi.baton.v1.Task.ActionStatusTask.annotations:type_name -> google.protobuf.Any - 49, // 90: c1.connectorapi.baton.v1.Task.CreateSyncDiffTask.annotations:type_name -> google.protobuf.Any - 40, // 91: c1.connectorapi.baton.v1.Task.CompactSyncs.compactable_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs.CompactableSync - 49, // 92: c1.connectorapi.baton.v1.Task.CompactSyncs.annotations:type_name -> google.protobuf.Any - 49, // 93: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata.annotations:type_name -> google.protobuf.Any - 49, // 94: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF.annotations:type_name -> google.protobuf.Any - 49, // 95: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.annotations:type_name -> google.protobuf.Any - 49, // 96: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.response:type_name -> google.protobuf.Any - 49, // 97: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.annotations:type_name -> google.protobuf.Any - 49, // 98: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.response:type_name -> google.protobuf.Any - 2, // 99: c1.connectorapi.baton.v1.BatonService.Hello:input_type -> c1.connectorapi.baton.v1.BatonServiceHelloRequest - 4, // 100: c1.connectorapi.baton.v1.BatonService.GetTask:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskRequest - 5, // 101: c1.connectorapi.baton.v1.BatonService.GetTasks:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksRequest - 8, // 102: c1.connectorapi.baton.v1.BatonService.Heartbeat:input_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest - 12, // 103: c1.connectorapi.baton.v1.BatonService.FinishTask:input_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest - 10, // 104: c1.connectorapi.baton.v1.BatonService.UploadAsset:input_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest - 14, // 105: c1.connectorapi.baton.v1.BatonService.StartDebugging:input_type -> c1.connectorapi.baton.v1.StartDebuggingRequest - 3, // 106: c1.connectorapi.baton.v1.BatonService.Hello:output_type -> c1.connectorapi.baton.v1.BatonServiceHelloResponse - 7, // 107: c1.connectorapi.baton.v1.BatonService.GetTask:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskResponse - 6, // 108: c1.connectorapi.baton.v1.BatonService.GetTasks:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksResponse - 9, // 109: c1.connectorapi.baton.v1.BatonService.Heartbeat:output_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse - 13, // 110: c1.connectorapi.baton.v1.BatonService.FinishTask:output_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse - 11, // 111: c1.connectorapi.baton.v1.BatonService.UploadAsset:output_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse - 15, // 112: c1.connectorapi.baton.v1.BatonService.StartDebugging:output_type -> c1.connectorapi.baton.v1.StartDebuggingResponse - 106, // [106:113] is the sub-list for method output_type - 99, // [99:106] is the sub-list for method input_type - 99, // [99:99] is the sub-list for extension type_name - 99, // [99:99] is the sub-list for extension extendee - 0, // [0:99] is the sub-list for field type_name + 39, // 20: c1.connectorapi.baton.v1.Task.compact_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs + 21, // 21: c1.connectorapi.baton.v1.Task.list_event_feeds:type_name -> c1.connectorapi.baton.v1.Task.ListEventFeedsTask + 20, // 22: c1.connectorapi.baton.v1.Task.list_events:type_name -> c1.connectorapi.baton.v1.Task.ListEventsTask + 28, // 23: c1.connectorapi.baton.v1.Task.issue_credential:type_name -> c1.connectorapi.baton.v1.Task.IssueCredentialTask + 41, // 24: c1.connectorapi.baton.v1.BatonServiceHelloRequest.build_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.BuildInfo + 42, // 25: c1.connectorapi.baton.v1.BatonServiceHelloRequest.os_info:type_name -> c1.connectorapi.baton.v1.BatonServiceHelloRequest.OSInfo + 48, // 26: c1.connectorapi.baton.v1.BatonServiceHelloRequest.connector_metadata:type_name -> c1.connector.v2.ConnectorMetadata + 49, // 27: c1.connectorapi.baton.v1.BatonServiceHelloRequest.annotations:type_name -> google.protobuf.Any + 49, // 28: c1.connectorapi.baton.v1.BatonServiceHelloResponse.annotations:type_name -> google.protobuf.Any + 49, // 29: c1.connectorapi.baton.v1.BatonServiceGetTasksRequest.annotations:type_name -> google.protobuf.Any + 1, // 30: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.tasks:type_name -> c1.connectorapi.baton.v1.Task + 50, // 31: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_poll:type_name -> google.protobuf.Duration + 50, // 32: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.next_heartbeat:type_name -> google.protobuf.Duration + 49, // 33: c1.connectorapi.baton.v1.BatonServiceGetTasksResponse.annotations:type_name -> google.protobuf.Any + 1, // 34: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.task:type_name -> c1.connectorapi.baton.v1.Task + 50, // 35: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_poll:type_name -> google.protobuf.Duration + 50, // 36: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.next_heartbeat:type_name -> google.protobuf.Duration + 49, // 37: c1.connectorapi.baton.v1.BatonServiceGetTaskResponse.annotations:type_name -> google.protobuf.Any + 49, // 38: c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest.annotations:type_name -> google.protobuf.Any + 50, // 39: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.next_heartbeat:type_name -> google.protobuf.Duration + 49, // 40: c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse.annotations:type_name -> google.protobuf.Any + 43, // 41: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.metadata:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata + 44, // 42: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.data:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadData + 45, // 43: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.eof:type_name -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF + 49, // 44: c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse.annotations:type_name -> google.protobuf.Any + 51, // 45: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.status:type_name -> google.rpc.Status + 46, // 46: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.error:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error + 47, // 47: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.success:type_name -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success + 49, // 48: c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse.annotations:type_name -> google.protobuf.Any + 49, // 49: c1.connectorapi.baton.v1.Task.NoneTask.annotations:type_name -> google.protobuf.Any + 49, // 50: c1.connectorapi.baton.v1.Task.HelloTask.annotations:type_name -> google.protobuf.Any + 49, // 51: c1.connectorapi.baton.v1.Task.SyncFullTask.annotations:type_name -> google.protobuf.Any + 52, // 52: c1.connectorapi.baton.v1.Task.SyncFullTask.targeted_sync_resources:type_name -> c1.connector.v2.Resource + 49, // 53: c1.connectorapi.baton.v1.Task.EventFeedTask.annotations:type_name -> google.protobuf.Any + 53, // 54: c1.connectorapi.baton.v1.Task.EventFeedTask.start_at:type_name -> google.protobuf.Timestamp + 49, // 55: c1.connectorapi.baton.v1.Task.ListEventsTask.annotations:type_name -> google.protobuf.Any + 53, // 56: c1.connectorapi.baton.v1.Task.ListEventsTask.start_at:type_name -> google.protobuf.Timestamp + 49, // 57: c1.connectorapi.baton.v1.Task.ListEventFeedsTask.annotations:type_name -> google.protobuf.Any + 54, // 58: c1.connectorapi.baton.v1.Task.GrantTask.entitlement:type_name -> c1.connector.v2.Entitlement + 52, // 59: c1.connectorapi.baton.v1.Task.GrantTask.principal:type_name -> c1.connector.v2.Resource + 49, // 60: c1.connectorapi.baton.v1.Task.GrantTask.annotations:type_name -> google.protobuf.Any + 50, // 61: c1.connectorapi.baton.v1.Task.GrantTask.duration:type_name -> google.protobuf.Duration + 55, // 62: c1.connectorapi.baton.v1.Task.RevokeTask.grant:type_name -> c1.connector.v2.Grant + 49, // 63: c1.connectorapi.baton.v1.Task.RevokeTask.annotations:type_name -> google.protobuf.Any + 56, // 64: c1.connectorapi.baton.v1.Task.CreateAccountTask.account_info:type_name -> c1.connector.v2.AccountInfo + 57, // 65: c1.connectorapi.baton.v1.Task.CreateAccountTask.credential_options:type_name -> c1.connector.v2.CredentialOptions + 58, // 66: c1.connectorapi.baton.v1.Task.CreateAccountTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig + 52, // 67: c1.connectorapi.baton.v1.Task.CreateResourceTask.resource:type_name -> c1.connector.v2.Resource + 59, // 68: c1.connectorapi.baton.v1.Task.DeleteResourceTask.resource_id:type_name -> c1.connector.v2.ResourceId + 59, // 69: c1.connectorapi.baton.v1.Task.DeleteResourceTask.parent_resource_id:type_name -> c1.connector.v2.ResourceId + 59, // 70: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.resource_id:type_name -> c1.connector.v2.ResourceId + 57, // 71: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.credential_options:type_name -> c1.connector.v2.CredentialOptions + 58, // 72: c1.connectorapi.baton.v1.Task.RotateCredentialsTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig + 59, // 73: c1.connectorapi.baton.v1.Task.IssueCredentialTask.identity_id:type_name -> c1.connector.v2.ResourceId + 60, // 74: c1.connectorapi.baton.v1.Task.IssueCredentialTask.credential_options:type_name -> c1.connector.v2.CredentialIssueOptions + 58, // 75: c1.connectorapi.baton.v1.Task.IssueCredentialTask.encryption_configs:type_name -> c1.connector.v2.EncryptionConfig + 53, // 76: c1.connectorapi.baton.v1.Task.IssueCredentialTask.expires_at:type_name -> google.protobuf.Timestamp + 61, // 77: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_request:type_name -> c1.connector.v2.TicketRequest + 62, // 78: c1.connectorapi.baton.v1.Task.CreateTicketTask.ticket_schema:type_name -> c1.connector.v2.TicketSchema + 49, // 79: c1.connectorapi.baton.v1.Task.CreateTicketTask.annotations:type_name -> google.protobuf.Any + 29, // 80: c1.connectorapi.baton.v1.Task.BulkCreateTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.CreateTicketTask + 33, // 81: c1.connectorapi.baton.v1.Task.BulkGetTicketsTask.ticket_requests:type_name -> c1.connectorapi.baton.v1.Task.GetTicketTask + 49, // 82: c1.connectorapi.baton.v1.Task.ListTicketSchemasTask.annotations:type_name -> google.protobuf.Any + 49, // 83: c1.connectorapi.baton.v1.Task.GetTicketTask.annotations:type_name -> google.protobuf.Any + 49, // 84: c1.connectorapi.baton.v1.Task.ActionListSchemasTask.annotations:type_name -> google.protobuf.Any + 49, // 85: c1.connectorapi.baton.v1.Task.ActionGetSchemaTask.annotations:type_name -> google.protobuf.Any + 63, // 86: c1.connectorapi.baton.v1.Task.ActionInvokeTask.args:type_name -> google.protobuf.Struct + 49, // 87: c1.connectorapi.baton.v1.Task.ActionInvokeTask.annotations:type_name -> google.protobuf.Any + 49, // 88: c1.connectorapi.baton.v1.Task.ActionStatusTask.annotations:type_name -> google.protobuf.Any + 49, // 89: c1.connectorapi.baton.v1.Task.CreateSyncDiffTask.annotations:type_name -> google.protobuf.Any + 40, // 90: c1.connectorapi.baton.v1.Task.CompactSyncs.compactable_syncs:type_name -> c1.connectorapi.baton.v1.Task.CompactSyncs.CompactableSync + 49, // 91: c1.connectorapi.baton.v1.Task.CompactSyncs.annotations:type_name -> google.protobuf.Any + 49, // 92: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadMetadata.annotations:type_name -> google.protobuf.Any + 49, // 93: c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest.UploadEOF.annotations:type_name -> google.protobuf.Any + 49, // 94: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.annotations:type_name -> google.protobuf.Any + 49, // 95: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Error.response:type_name -> google.protobuf.Any + 49, // 96: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.annotations:type_name -> google.protobuf.Any + 49, // 97: c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest.Success.response:type_name -> google.protobuf.Any + 2, // 98: c1.connectorapi.baton.v1.BatonService.Hello:input_type -> c1.connectorapi.baton.v1.BatonServiceHelloRequest + 4, // 99: c1.connectorapi.baton.v1.BatonService.GetTask:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskRequest + 5, // 100: c1.connectorapi.baton.v1.BatonService.GetTasks:input_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksRequest + 8, // 101: c1.connectorapi.baton.v1.BatonService.Heartbeat:input_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatRequest + 12, // 102: c1.connectorapi.baton.v1.BatonService.FinishTask:input_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskRequest + 10, // 103: c1.connectorapi.baton.v1.BatonService.UploadAsset:input_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetRequest + 14, // 104: c1.connectorapi.baton.v1.BatonService.StartDebugging:input_type -> c1.connectorapi.baton.v1.StartDebuggingRequest + 3, // 105: c1.connectorapi.baton.v1.BatonService.Hello:output_type -> c1.connectorapi.baton.v1.BatonServiceHelloResponse + 7, // 106: c1.connectorapi.baton.v1.BatonService.GetTask:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTaskResponse + 6, // 107: c1.connectorapi.baton.v1.BatonService.GetTasks:output_type -> c1.connectorapi.baton.v1.BatonServiceGetTasksResponse + 9, // 108: c1.connectorapi.baton.v1.BatonService.Heartbeat:output_type -> c1.connectorapi.baton.v1.BatonServiceHeartbeatResponse + 13, // 109: c1.connectorapi.baton.v1.BatonService.FinishTask:output_type -> c1.connectorapi.baton.v1.BatonServiceFinishTaskResponse + 11, // 110: c1.connectorapi.baton.v1.BatonService.UploadAsset:output_type -> c1.connectorapi.baton.v1.BatonServiceUploadAssetResponse + 15, // 111: c1.connectorapi.baton.v1.BatonService.StartDebugging:output_type -> c1.connectorapi.baton.v1.StartDebuggingResponse + 105, // [105:112] is the sub-list for method output_type + 98, // [98:105] is the sub-list for method input_type + 98, // [98:98] is the sub-list for extension type_name + 98, // [98:98] is the sub-list for extension extendee + 0, // [0:98] is the sub-list for field type_name } func init() { file_c1_connectorapi_baton_v1_baton_proto_init() } @@ -6100,7 +6059,6 @@ func file_c1_connectorapi_baton_v1_baton_proto_init() { (*task_ActionGetSchema)(nil), (*task_ActionInvoke)(nil), (*task_ActionStatus)(nil), - (*task_CreateSyncDiff)(nil), (*task_CompactSyncs_)(nil), (*task_ListEventFeeds)(nil), (*task_ListEvents)(nil), diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.go index 13247122..d6db156d 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.go @@ -40,11 +40,18 @@ const ( type SyncType int32 const ( - SyncType_SYNC_TYPE_UNSPECIFIED SyncType = 0 - SyncType_SYNC_TYPE_FULL SyncType = 1 - SyncType_SYNC_TYPE_PARTIAL SyncType = 2 - SyncType_SYNC_TYPE_RESOURCES_ONLY SyncType = 3 - SyncType_SYNC_TYPE_PARTIAL_UPSERTS SyncType = 4 + SyncType_SYNC_TYPE_UNSPECIFIED SyncType = 0 + SyncType_SYNC_TYPE_FULL SyncType = 1 + SyncType_SYNC_TYPE_PARTIAL SyncType = 2 + SyncType_SYNC_TYPE_RESOURCES_ONLY SyncType = 3 + // Deprecated: 4 and 5 were the diff-sync pair; diff-sync support was + // removed and nothing produces or consumes them. Kept (rather than + // reserved) so the buf breaking policy can keep forbidding enum value + // deletion repo-wide. + // + // Deprecated: Marked as deprecated in c1/storage/v3/records.proto. + SyncType_SYNC_TYPE_PARTIAL_UPSERTS SyncType = 4 + // Deprecated: Marked as deprecated in c1/storage/v3/records.proto. SyncType_SYNC_TYPE_PARTIAL_DELETIONS SyncType = 5 ) @@ -97,6 +104,7 @@ const ( StatusRecord_RESOURCE_STATUS_ENABLED StatusRecord_ResourceStatus = 1 StatusRecord_RESOURCE_STATUS_DISABLED StatusRecord_ResourceStatus = 2 StatusRecord_RESOURCE_STATUS_DELETED StatusRecord_ResourceStatus = 3 + StatusRecord_RESOURCE_STATUS_PENDING StatusRecord_ResourceStatus = 4 ) // Enum value maps for StatusRecord_ResourceStatus. @@ -106,12 +114,14 @@ var ( 1: "RESOURCE_STATUS_ENABLED", 2: "RESOURCE_STATUS_DISABLED", 3: "RESOURCE_STATUS_DELETED", + 4: "RESOURCE_STATUS_PENDING", } StatusRecord_ResourceStatus_value = map[string]int32{ "RESOURCE_STATUS_UNSPECIFIED": 0, "RESOURCE_STATUS_ENABLED": 1, "RESOURCE_STATUS_DISABLED": 2, "RESOURCE_STATUS_DELETED": 3, + "RESOURCE_STATUS_PENDING": 4, } ) @@ -591,8 +601,10 @@ type ResourceRecord struct { // to 12 when profile/status/created_at (9-11) landed on main first. // No artifact was ever written with the old number. SourceScopeKey string `protobuf:"bytes,12,opt,name=source_scope_key,json=sourceScopeKey,proto3" json:"source_scope_key,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // External ID of the resource icon asset. This must point to an asset that is an image. + IconAssetExternalId string `protobuf:"bytes,13,opt,name=icon_asset_external_id,json=iconAssetExternalId,proto3" json:"icon_asset_external_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ResourceRecord) Reset() { @@ -697,6 +709,13 @@ func (x *ResourceRecord) GetSourceScopeKey() string { return "" } +func (x *ResourceRecord) GetIconAssetExternalId() string { + if x != nil { + return x.IconAssetExternalId + } + return "" +} + func (x *ResourceRecord) SetResourceTypeId(v string) { x.ResourceTypeId = v } @@ -741,6 +760,10 @@ func (x *ResourceRecord) SetSourceScopeKey(v string) { x.SourceScopeKey = v } +func (x *ResourceRecord) SetIconAssetExternalId(v string) { + x.IconAssetExternalId = v +} + func (x *ResourceRecord) HasParent() bool { if x == nil { return false @@ -818,6 +841,8 @@ type ResourceRecord_builder struct { // to 12 when profile/status/created_at (9-11) landed on main first. // No artifact was ever written with the old number. SourceScopeKey string + // External ID of the resource icon asset. This must point to an asset that is an image. + IconAssetExternalId string } func (b0 ResourceRecord_builder) Build() *ResourceRecord { @@ -835,6 +860,7 @@ func (b0 ResourceRecord_builder) Build() *ResourceRecord { x.Status = b.Status x.CreatedAt = b.CreatedAt x.SourceScopeKey = b.SourceScopeKey + x.IconAssetExternalId = b.IconAssetExternalId return m0 } @@ -1455,8 +1481,11 @@ type SyncRunRecord struct { StartedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` EndedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=ended_at,json=endedAt,proto3" json:"ended_at,omitempty"` SyncToken string `protobuf:"bytes,6,opt,name=sync_token,json=syncToken,proto3" json:"sync_token,omitempty"` - SupportsDiff bool `protobuf:"varint,7,opt,name=supports_diff,json=supportsDiff,proto3" json:"supports_diff,omitempty"` - LinkedSyncId string `protobuf:"bytes,8,opt,name=linked_sync_id,json=linkedSyncId,proto3" json:"linked_sync_id,omitempty"` + // supports_diff marks a sync whose data collection completed with + // SQL-layer grant metadata populated. The name is historical (it once + // gated diff-sync generation, since removed); today it gates + // `baton rollback-expansion`. + SupportsDiff bool `protobuf:"varint,7,opt,name=supports_diff,json=supportsDiff,proto3" json:"supports_diff,omitempty"` // compacted marks a sync produced by compaction (fold or rebuild) // rather than by a real connector run. Compacted artifacts are // keep-newer UPSERT merges — base rows a newer input deleted survive — @@ -1561,13 +1590,6 @@ func (x *SyncRunRecord) GetSupportsDiff() bool { return false } -func (x *SyncRunRecord) GetLinkedSyncId() string { - if x != nil { - return x.LinkedSyncId - } - return "" -} - func (x *SyncRunRecord) GetCompacted() bool { if x != nil { return x.Compacted @@ -1624,10 +1646,6 @@ func (x *SyncRunRecord) SetSupportsDiff(v bool) { x.SupportsDiff = v } -func (x *SyncRunRecord) SetLinkedSyncId(v string) { - x.LinkedSyncId = v -} - func (x *SyncRunRecord) SetCompacted(v bool) { x.Compacted = v } @@ -1675,8 +1693,11 @@ type SyncRunRecord_builder struct { StartedAt *timestamppb.Timestamp EndedAt *timestamppb.Timestamp SyncToken string + // supports_diff marks a sync whose data collection completed with + // SQL-layer grant metadata populated. The name is historical (it once + // gated diff-sync generation, since removed); today it gates + // `baton rollback-expansion`. SupportsDiff bool - LinkedSyncId string // compacted marks a sync produced by compaction (fold or rebuild) // rather than by a real connector run. Compacted artifacts are // keep-newer UPSERT merges — base rows a newer input deleted survive — @@ -1716,7 +1737,6 @@ func (b0 SyncRunRecord_builder) Build() *SyncRunRecord { x.EndedAt = b.EndedAt x.SyncToken = b.SyncToken x.SupportsDiff = b.SupportsDiff - x.LinkedSyncId = b.LinkedSyncId x.Compacted = b.Compacted x.IngestInvariantGeneration = b.IngestInvariantGeneration x.IngestInvariantCoverage = b.IngestInvariantCoverage @@ -2728,15 +2748,16 @@ var File_c1_storage_v3_records_proto protoreflect.FileDescriptor const file_c1_storage_v3_records_proto_rawDesc = "" + "\n" + - "\x1bc1/storage/v3/records.proto\x12\rc1.storage.v3\x1a\x1bc1/storage/v3/options.proto\x1a\x18c1/storage/v3/refs.proto\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf8\x01\n" + + "\x1bc1/storage/v3/records.proto\x12\rc1.storage.v3\x1a\x1bc1/storage/v3/options.proto\x1a\x18c1/storage/v3/refs.proto\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x95\x02\n" + "\fStatusRecord\x12B\n" + "\x06status\x18\x01 \x01(\x0e2*.c1.storage.v3.StatusRecord.ResourceStatusR\x06status\x12\x18\n" + - "\adetails\x18\x02 \x01(\tR\adetails\"\x89\x01\n" + + "\adetails\x18\x02 \x01(\tR\adetails\"\xa6\x01\n" + "\x0eResourceStatus\x12\x1f\n" + "\x1bRESOURCE_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n" + "\x17RESOURCE_STATUS_ENABLED\x10\x01\x12\x1c\n" + "\x18RESOURCE_STATUS_DISABLED\x10\x02\x12\x1b\n" + - "\x17RESOURCE_STATUS_DELETED\x10\x03\"\x86\x01\n" + + "\x17RESOURCE_STATUS_DELETED\x10\x03\x12\x1b\n" + + "\x17RESOURCE_STATUS_PENDING\x10\x04\"\x86\x01\n" + "\x15GrantExpandableRecord\x12'\n" + "\x0fentitlement_ids\x18\x01 \x03(\tR\x0eentitlementIds\x12\x18\n" + "\ashallow\x18\x02 \x01(\bR\ashallow\x12*\n" + @@ -2756,7 +2777,7 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\rdiscovered_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\fdiscoveredAt\x12 \n" + "\vdescription\x18\a \x01(\tR\vdescription\x12-\n" + "\x12sourced_externally\x18\b \x01(\bR\x11sourcedExternally:!\x82\xf9+\x1d\n" + - "\x0eresource_types\x12\vexternal_idJ\x04\b\x01\x10\x02R\async_id\"\xb8\x05\n" + + "\x0eresource_types\x12\vexternal_idJ\x04\b\x01\x10\x02R\async_id\"\xed\x05\n" + "\x0eResourceRecord\x12(\n" + "\x10resource_type_id\x18\x02 \x01(\tR\x0eresourceTypeId\x12\x1f\n" + "\vresource_id\x18\x03 \x01(\tR\n" + @@ -2773,7 +2794,8 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\n" + "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12W\n" + "\x10source_scope_key\x18\f \x01(\tB-\x8a\xf9+)\n" + - "\x0fby_source_scope\"\x16source_scope_key != ''R\x0esourceScopeKey:.\x82\xf9+*\n" + + "\x0fby_source_scope\"\x16source_scope_key != ''R\x0esourceScopeKey\x123\n" + + "\x16icon_asset_external_id\x18\r \x01(\tR\x13iconAssetExternalId:.\x82\xf9+*\n" + "\tresources\x12\x10resource_type_id\x12\vresource_idJ\x04\b\x01\x10\x02R\async_id\"\xd7\x04\n" + "\x11EntitlementRecord\x12\x1f\n" + "\vexternal_id\x18\x02 \x01(\tR\n" + @@ -2818,7 +2840,7 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\fcontent_type\x18\x03 \x01(\tR\vcontentType\x12\x12\n" + "\x04data\x18\x04 \x01(\fR\x04data\x12?\n" + "\rdiscovered_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\fdiscoveredAt:\"\x82\xf9+\x1e\n" + - "\x06assets\x12\async_id\x12\vexternal_id\"\xbf\x04\n" + + "\x06assets\x12\async_id\x12\vexternal_id\"\xaf\x04\n" + "\rSyncRunRecord\x12\x17\n" + "\async_id\x18\x01 \x01(\tR\x06syncId\x12+\n" + "\x04type\x18\x02 \x01(\x0e2\x17.c1.storage.v3.SyncTypeR\x04type\x12$\n" + @@ -2828,14 +2850,13 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\bended_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\aendedAt\x12\x1d\n" + "\n" + "sync_token\x18\x06 \x01(\tR\tsyncToken\x12#\n" + - "\rsupports_diff\x18\a \x01(\bR\fsupportsDiff\x12$\n" + - "\x0elinked_sync_id\x18\b \x01(\tR\flinkedSyncId\x12\x1c\n" + + "\rsupports_diff\x18\a \x01(\bR\fsupportsDiff\x12\x1c\n" + "\tcompacted\x18\t \x01(\bR\tcompacted\x12>\n" + "\x1bingest_invariant_generation\x18\n" + " \x01(\tR\x19ingestInvariantGeneration\x12:\n" + "\x19ingest_invariant_coverage\x18\v \x03(\tR\x17ingestInvariantCoverage\x122\n" + "\x15ingest_invariant_mode\x18\f \x01(\tR\x13ingestInvariantMode:\x18\x82\xf9+\x14\n" + - "\tsync_runs\x12\async_id\"\xfe\v\n" + + "\tsync_runs\x12\async_idJ\x04\b\b\x10\tR\x0elinked_sync_id\"\xfe\v\n" + "\x0fSyncStatsRecord\x12\x17\n" + "\async_id\x18\x01 \x01(\tR\x06syncId\x12%\n" + "\x0eresource_types\x18\x02 \x01(\x03R\rresourceTypes\x12\x1c\n" + @@ -2907,14 +2928,14 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\x1cconnector_config_fingerprint\x18\x03 \x01(\tR\x1aconnectorConfigFingerprint\x12D\n" + "\x1esdk_materialization_generation\x18\x04 \x01(\tR\x1csdkMaterializationGeneration\x12<\n" + "\x1async_selection_fingerprint\x18\x05 \x01(\tR\x18syncSelectionFingerprint:\x1d\x82\xf9+\x19\n" + - "\x13source_cache_compat\x12\x02id*\xae\x01\n" + + "\x13source_cache_compat\x12\x02id*\xb6\x01\n" + "\bSyncType\x12\x19\n" + "\x15SYNC_TYPE_UNSPECIFIED\x10\x00\x12\x12\n" + "\x0eSYNC_TYPE_FULL\x10\x01\x12\x15\n" + "\x11SYNC_TYPE_PARTIAL\x10\x02\x12\x1c\n" + - "\x18SYNC_TYPE_RESOURCES_ONLY\x10\x03\x12\x1d\n" + - "\x19SYNC_TYPE_PARTIAL_UPSERTS\x10\x04\x12\x1f\n" + - "\x1bSYNC_TYPE_PARTIAL_DELETIONS\x10\x05B4Z2github.com/conductorone/baton-sdk/pb/c1/storage/v3b\x06proto3" + "\x18SYNC_TYPE_RESOURCES_ONLY\x10\x03\x12!\n" + + "\x19SYNC_TYPE_PARTIAL_UPSERTS\x10\x04\x1a\x02\b\x01\x12#\n" + + "\x1bSYNC_TYPE_PARTIAL_DELETIONS\x10\x05\x1a\x02\b\x01B4Z2github.com/conductorone/baton-sdk/pb/c1/storage/v3b\x06proto3" var file_c1_storage_v3_records_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_c1_storage_v3_records_proto_msgTypes = make([]protoimpl.MessageInfo, 22) diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.validate.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.validate.go index e3d7f2f7..d20b2648 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.validate.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records.pb.validate.go @@ -736,6 +736,8 @@ func (m *ResourceRecord) validate(all bool) error { // no validation rules for SourceScopeKey + // no validation rules for IconAssetExternalId + if len(errors) > 0 { return ResourceRecordMultiError(errors) } @@ -1547,8 +1549,6 @@ func (m *SyncRunRecord) validate(all bool) error { // no validation rules for SupportsDiff - // no validation rules for LinkedSyncId - // no validation rules for Compacted // no validation rules for IngestInvariantGeneration diff --git a/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records_protoopaque.pb.go b/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records_protoopaque.pb.go index e6df94ce..ea62c330 100644 --- a/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records_protoopaque.pb.go +++ b/vendor/github.com/conductorone/baton-sdk/pb/c1/storage/v3/records_protoopaque.pb.go @@ -40,11 +40,18 @@ const ( type SyncType int32 const ( - SyncType_SYNC_TYPE_UNSPECIFIED SyncType = 0 - SyncType_SYNC_TYPE_FULL SyncType = 1 - SyncType_SYNC_TYPE_PARTIAL SyncType = 2 - SyncType_SYNC_TYPE_RESOURCES_ONLY SyncType = 3 - SyncType_SYNC_TYPE_PARTIAL_UPSERTS SyncType = 4 + SyncType_SYNC_TYPE_UNSPECIFIED SyncType = 0 + SyncType_SYNC_TYPE_FULL SyncType = 1 + SyncType_SYNC_TYPE_PARTIAL SyncType = 2 + SyncType_SYNC_TYPE_RESOURCES_ONLY SyncType = 3 + // Deprecated: 4 and 5 were the diff-sync pair; diff-sync support was + // removed and nothing produces or consumes them. Kept (rather than + // reserved) so the buf breaking policy can keep forbidding enum value + // deletion repo-wide. + // + // Deprecated: Marked as deprecated in c1/storage/v3/records.proto. + SyncType_SYNC_TYPE_PARTIAL_UPSERTS SyncType = 4 + // Deprecated: Marked as deprecated in c1/storage/v3/records.proto. SyncType_SYNC_TYPE_PARTIAL_DELETIONS SyncType = 5 ) @@ -97,6 +104,7 @@ const ( StatusRecord_RESOURCE_STATUS_ENABLED StatusRecord_ResourceStatus = 1 StatusRecord_RESOURCE_STATUS_DISABLED StatusRecord_ResourceStatus = 2 StatusRecord_RESOURCE_STATUS_DELETED StatusRecord_ResourceStatus = 3 + StatusRecord_RESOURCE_STATUS_PENDING StatusRecord_ResourceStatus = 4 ) // Enum value maps for StatusRecord_ResourceStatus. @@ -106,12 +114,14 @@ var ( 1: "RESOURCE_STATUS_ENABLED", 2: "RESOURCE_STATUS_DISABLED", 3: "RESOURCE_STATUS_DELETED", + 4: "RESOURCE_STATUS_PENDING", } StatusRecord_ResourceStatus_value = map[string]int32{ "RESOURCE_STATUS_UNSPECIFIED": 0, "RESOURCE_STATUS_ENABLED": 1, "RESOURCE_STATUS_DISABLED": 2, "RESOURCE_STATUS_DELETED": 3, + "RESOURCE_STATUS_PENDING": 4, } ) @@ -564,20 +574,21 @@ func (b0 ResourceTypeRecord_builder) Build() *ResourceTypeRecord { } type ResourceRecord struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_ResourceTypeId string `protobuf:"bytes,2,opt,name=resource_type_id,json=resourceTypeId,proto3"` - xxx_hidden_ResourceId string `protobuf:"bytes,3,opt,name=resource_id,json=resourceId,proto3"` - xxx_hidden_DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3"` - xxx_hidden_Description string `protobuf:"bytes,5,opt,name=description,proto3"` - xxx_hidden_Parent *ResourceRef `protobuf:"bytes,6,opt,name=parent,proto3"` - xxx_hidden_Annotations *[]*anypb.Any `protobuf:"bytes,7,rep,name=annotations,proto3"` - xxx_hidden_DiscoveredAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=discovered_at,json=discoveredAt,proto3"` - xxx_hidden_Profile *structpb.Struct `protobuf:"bytes,9,opt,name=profile,proto3"` - xxx_hidden_Status *StatusRecord `protobuf:"bytes,10,opt,name=status,proto3"` - xxx_hidden_CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3"` - xxx_hidden_SourceScopeKey string `protobuf:"bytes,12,opt,name=source_scope_key,json=sourceScopeKey,proto3"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_ResourceTypeId string `protobuf:"bytes,2,opt,name=resource_type_id,json=resourceTypeId,proto3"` + xxx_hidden_ResourceId string `protobuf:"bytes,3,opt,name=resource_id,json=resourceId,proto3"` + xxx_hidden_DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3"` + xxx_hidden_Description string `protobuf:"bytes,5,opt,name=description,proto3"` + xxx_hidden_Parent *ResourceRef `protobuf:"bytes,6,opt,name=parent,proto3"` + xxx_hidden_Annotations *[]*anypb.Any `protobuf:"bytes,7,rep,name=annotations,proto3"` + xxx_hidden_DiscoveredAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=discovered_at,json=discoveredAt,proto3"` + xxx_hidden_Profile *structpb.Struct `protobuf:"bytes,9,opt,name=profile,proto3"` + xxx_hidden_Status *StatusRecord `protobuf:"bytes,10,opt,name=status,proto3"` + xxx_hidden_CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3"` + xxx_hidden_SourceScopeKey string `protobuf:"bytes,12,opt,name=source_scope_key,json=sourceScopeKey,proto3"` + xxx_hidden_IconAssetExternalId string `protobuf:"bytes,13,opt,name=icon_asset_external_id,json=iconAssetExternalId,proto3"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ResourceRecord) Reset() { @@ -684,6 +695,13 @@ func (x *ResourceRecord) GetSourceScopeKey() string { return "" } +func (x *ResourceRecord) GetIconAssetExternalId() string { + if x != nil { + return x.xxx_hidden_IconAssetExternalId + } + return "" +} + func (x *ResourceRecord) SetResourceTypeId(v string) { x.xxx_hidden_ResourceTypeId = v } @@ -728,6 +746,10 @@ func (x *ResourceRecord) SetSourceScopeKey(v string) { x.xxx_hidden_SourceScopeKey = v } +func (x *ResourceRecord) SetIconAssetExternalId(v string) { + x.xxx_hidden_IconAssetExternalId = v +} + func (x *ResourceRecord) HasParent() bool { if x == nil { return false @@ -805,6 +827,8 @@ type ResourceRecord_builder struct { // to 12 when profile/status/created_at (9-11) landed on main first. // No artifact was ever written with the old number. SourceScopeKey string + // External ID of the resource icon asset. This must point to an asset that is an image. + IconAssetExternalId string } func (b0 ResourceRecord_builder) Build() *ResourceRecord { @@ -822,6 +846,7 @@ func (b0 ResourceRecord_builder) Build() *ResourceRecord { x.xxx_hidden_Status = b.Status x.xxx_hidden_CreatedAt = b.CreatedAt x.xxx_hidden_SourceScopeKey = b.SourceScopeKey + x.xxx_hidden_IconAssetExternalId = b.IconAssetExternalId return m0 } @@ -1415,7 +1440,6 @@ type SyncRunRecord struct { xxx_hidden_EndedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=ended_at,json=endedAt,proto3"` xxx_hidden_SyncToken string `protobuf:"bytes,6,opt,name=sync_token,json=syncToken,proto3"` xxx_hidden_SupportsDiff bool `protobuf:"varint,7,opt,name=supports_diff,json=supportsDiff,proto3"` - xxx_hidden_LinkedSyncId string `protobuf:"bytes,8,opt,name=linked_sync_id,json=linkedSyncId,proto3"` xxx_hidden_Compacted bool `protobuf:"varint,9,opt,name=compacted,proto3"` xxx_hidden_IngestInvariantGeneration string `protobuf:"bytes,10,opt,name=ingest_invariant_generation,json=ingestInvariantGeneration,proto3"` xxx_hidden_IngestInvariantCoverage []string `protobuf:"bytes,11,rep,name=ingest_invariant_coverage,json=ingestInvariantCoverage,proto3"` @@ -1498,13 +1522,6 @@ func (x *SyncRunRecord) GetSupportsDiff() bool { return false } -func (x *SyncRunRecord) GetLinkedSyncId() string { - if x != nil { - return x.xxx_hidden_LinkedSyncId - } - return "" -} - func (x *SyncRunRecord) GetCompacted() bool { if x != nil { return x.xxx_hidden_Compacted @@ -1561,10 +1578,6 @@ func (x *SyncRunRecord) SetSupportsDiff(v bool) { x.xxx_hidden_SupportsDiff = v } -func (x *SyncRunRecord) SetLinkedSyncId(v string) { - x.xxx_hidden_LinkedSyncId = v -} - func (x *SyncRunRecord) SetCompacted(v bool) { x.xxx_hidden_Compacted = v } @@ -1612,8 +1625,11 @@ type SyncRunRecord_builder struct { StartedAt *timestamppb.Timestamp EndedAt *timestamppb.Timestamp SyncToken string + // supports_diff marks a sync whose data collection completed with + // SQL-layer grant metadata populated. The name is historical (it once + // gated diff-sync generation, since removed); today it gates + // `baton rollback-expansion`. SupportsDiff bool - LinkedSyncId string // compacted marks a sync produced by compaction (fold or rebuild) // rather than by a real connector run. Compacted artifacts are // keep-newer UPSERT merges — base rows a newer input deleted survive — @@ -1653,7 +1669,6 @@ func (b0 SyncRunRecord_builder) Build() *SyncRunRecord { x.xxx_hidden_EndedAt = b.EndedAt x.xxx_hidden_SyncToken = b.SyncToken x.xxx_hidden_SupportsDiff = b.SupportsDiff - x.xxx_hidden_LinkedSyncId = b.LinkedSyncId x.xxx_hidden_Compacted = b.Compacted x.xxx_hidden_IngestInvariantGeneration = b.IngestInvariantGeneration x.xxx_hidden_IngestInvariantCoverage = b.IngestInvariantCoverage @@ -2618,15 +2633,16 @@ var File_c1_storage_v3_records_proto protoreflect.FileDescriptor const file_c1_storage_v3_records_proto_rawDesc = "" + "\n" + - "\x1bc1/storage/v3/records.proto\x12\rc1.storage.v3\x1a\x1bc1/storage/v3/options.proto\x1a\x18c1/storage/v3/refs.proto\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf8\x01\n" + + "\x1bc1/storage/v3/records.proto\x12\rc1.storage.v3\x1a\x1bc1/storage/v3/options.proto\x1a\x18c1/storage/v3/refs.proto\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x95\x02\n" + "\fStatusRecord\x12B\n" + "\x06status\x18\x01 \x01(\x0e2*.c1.storage.v3.StatusRecord.ResourceStatusR\x06status\x12\x18\n" + - "\adetails\x18\x02 \x01(\tR\adetails\"\x89\x01\n" + + "\adetails\x18\x02 \x01(\tR\adetails\"\xa6\x01\n" + "\x0eResourceStatus\x12\x1f\n" + "\x1bRESOURCE_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n" + "\x17RESOURCE_STATUS_ENABLED\x10\x01\x12\x1c\n" + "\x18RESOURCE_STATUS_DISABLED\x10\x02\x12\x1b\n" + - "\x17RESOURCE_STATUS_DELETED\x10\x03\"\x86\x01\n" + + "\x17RESOURCE_STATUS_DELETED\x10\x03\x12\x1b\n" + + "\x17RESOURCE_STATUS_PENDING\x10\x04\"\x86\x01\n" + "\x15GrantExpandableRecord\x12'\n" + "\x0fentitlement_ids\x18\x01 \x03(\tR\x0eentitlementIds\x12\x18\n" + "\ashallow\x18\x02 \x01(\bR\ashallow\x12*\n" + @@ -2646,7 +2662,7 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\rdiscovered_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\fdiscoveredAt\x12 \n" + "\vdescription\x18\a \x01(\tR\vdescription\x12-\n" + "\x12sourced_externally\x18\b \x01(\bR\x11sourcedExternally:!\x82\xf9+\x1d\n" + - "\x0eresource_types\x12\vexternal_idJ\x04\b\x01\x10\x02R\async_id\"\xb8\x05\n" + + "\x0eresource_types\x12\vexternal_idJ\x04\b\x01\x10\x02R\async_id\"\xed\x05\n" + "\x0eResourceRecord\x12(\n" + "\x10resource_type_id\x18\x02 \x01(\tR\x0eresourceTypeId\x12\x1f\n" + "\vresource_id\x18\x03 \x01(\tR\n" + @@ -2663,7 +2679,8 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\n" + "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12W\n" + "\x10source_scope_key\x18\f \x01(\tB-\x8a\xf9+)\n" + - "\x0fby_source_scope\"\x16source_scope_key != ''R\x0esourceScopeKey:.\x82\xf9+*\n" + + "\x0fby_source_scope\"\x16source_scope_key != ''R\x0esourceScopeKey\x123\n" + + "\x16icon_asset_external_id\x18\r \x01(\tR\x13iconAssetExternalId:.\x82\xf9+*\n" + "\tresources\x12\x10resource_type_id\x12\vresource_idJ\x04\b\x01\x10\x02R\async_id\"\xd7\x04\n" + "\x11EntitlementRecord\x12\x1f\n" + "\vexternal_id\x18\x02 \x01(\tR\n" + @@ -2708,7 +2725,7 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\fcontent_type\x18\x03 \x01(\tR\vcontentType\x12\x12\n" + "\x04data\x18\x04 \x01(\fR\x04data\x12?\n" + "\rdiscovered_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\fdiscoveredAt:\"\x82\xf9+\x1e\n" + - "\x06assets\x12\async_id\x12\vexternal_id\"\xbf\x04\n" + + "\x06assets\x12\async_id\x12\vexternal_id\"\xaf\x04\n" + "\rSyncRunRecord\x12\x17\n" + "\async_id\x18\x01 \x01(\tR\x06syncId\x12+\n" + "\x04type\x18\x02 \x01(\x0e2\x17.c1.storage.v3.SyncTypeR\x04type\x12$\n" + @@ -2718,14 +2735,13 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\bended_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\aendedAt\x12\x1d\n" + "\n" + "sync_token\x18\x06 \x01(\tR\tsyncToken\x12#\n" + - "\rsupports_diff\x18\a \x01(\bR\fsupportsDiff\x12$\n" + - "\x0elinked_sync_id\x18\b \x01(\tR\flinkedSyncId\x12\x1c\n" + + "\rsupports_diff\x18\a \x01(\bR\fsupportsDiff\x12\x1c\n" + "\tcompacted\x18\t \x01(\bR\tcompacted\x12>\n" + "\x1bingest_invariant_generation\x18\n" + " \x01(\tR\x19ingestInvariantGeneration\x12:\n" + "\x19ingest_invariant_coverage\x18\v \x03(\tR\x17ingestInvariantCoverage\x122\n" + "\x15ingest_invariant_mode\x18\f \x01(\tR\x13ingestInvariantMode:\x18\x82\xf9+\x14\n" + - "\tsync_runs\x12\async_id\"\xfe\v\n" + + "\tsync_runs\x12\async_idJ\x04\b\b\x10\tR\x0elinked_sync_id\"\xfe\v\n" + "\x0fSyncStatsRecord\x12\x17\n" + "\async_id\x18\x01 \x01(\tR\x06syncId\x12%\n" + "\x0eresource_types\x18\x02 \x01(\x03R\rresourceTypes\x12\x1c\n" + @@ -2797,14 +2813,14 @@ const file_c1_storage_v3_records_proto_rawDesc = "" + "\x1cconnector_config_fingerprint\x18\x03 \x01(\tR\x1aconnectorConfigFingerprint\x12D\n" + "\x1esdk_materialization_generation\x18\x04 \x01(\tR\x1csdkMaterializationGeneration\x12<\n" + "\x1async_selection_fingerprint\x18\x05 \x01(\tR\x18syncSelectionFingerprint:\x1d\x82\xf9+\x19\n" + - "\x13source_cache_compat\x12\x02id*\xae\x01\n" + + "\x13source_cache_compat\x12\x02id*\xb6\x01\n" + "\bSyncType\x12\x19\n" + "\x15SYNC_TYPE_UNSPECIFIED\x10\x00\x12\x12\n" + "\x0eSYNC_TYPE_FULL\x10\x01\x12\x15\n" + "\x11SYNC_TYPE_PARTIAL\x10\x02\x12\x1c\n" + - "\x18SYNC_TYPE_RESOURCES_ONLY\x10\x03\x12\x1d\n" + - "\x19SYNC_TYPE_PARTIAL_UPSERTS\x10\x04\x12\x1f\n" + - "\x1bSYNC_TYPE_PARTIAL_DELETIONS\x10\x05B4Z2github.com/conductorone/baton-sdk/pb/c1/storage/v3b\x06proto3" + "\x18SYNC_TYPE_RESOURCES_ONLY\x10\x03\x12!\n" + + "\x19SYNC_TYPE_PARTIAL_UPSERTS\x10\x04\x1a\x02\b\x01\x12#\n" + + "\x1bSYNC_TYPE_PARTIAL_DELETIONS\x10\x05\x1a\x02\b\x01B4Z2github.com/conductorone/baton-sdk/pb/c1/storage/v3b\x06proto3" var file_c1_storage_v3_records_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_c1_storage_v3_records_proto_msgTypes = make([]protoimpl.MessageInfo, 22) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go b/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go index 22386800..2455ba43 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/cli/commands.go @@ -349,14 +349,6 @@ func MakeMainCommand[T field.Configurable]( opts = append(opts, connectorrunner.WithTicketingEnabled(), connectorrunner.WithGetTicket(v.GetString("ticket-id"))) - case v.GetBool("diff-syncs"): - opts = append(opts, - connectorrunner.WithDiffSyncs( - v.GetString("file"), - v.GetString("base-sync-id"), - v.GetString("applied-sync-id"), - ), - ) case v.GetBool("compact-syncs"): opts = append(opts, connectorrunner.WithSyncCompactor( diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/config/config.go b/vendor/github.com/conductorone/baton-sdk/pkg/config/config.go index f8c7c77b..200392a0 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/config/config.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/config/config.go @@ -12,6 +12,7 @@ import ( "github.com/conductorone/baton-sdk/pkg/cli" "github.com/conductorone/baton-sdk/pkg/connectorbuilder" "github.com/conductorone/baton-sdk/pkg/connectorrunner" + "github.com/conductorone/baton-sdk/pkg/exit" "github.com/conductorone/baton-sdk/pkg/field" "github.com/conductorone/baton-sdk/pkg/types" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" @@ -60,8 +61,7 @@ func RunConnector[T field.Configurable]( _, cmd, err := DefineConfigurationV2(ctx, connectorName, f, schema, options...) if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + exit.LogExit(err) return } @@ -69,8 +69,7 @@ func RunConnector[T field.Configurable]( err = cmd.Execute() if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + exit.LogExit(err) } } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/connectorrunner/runner.go b/vendor/github.com/conductorone/baton-sdk/pkg/connectorrunner/runner.go index afe734f1..be4fee2e 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/connectorrunner/runner.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/connectorrunner/runner.go @@ -384,11 +384,6 @@ type eventStreamConfig struct { cursor string } -type syncDifferConfig struct { - baseSyncID string - appliedSyncID string -} - type syncCompactorConfig struct { filePaths []string syncIDs []string @@ -419,7 +414,6 @@ type runnerConfig struct { bulkCreateTicketConfig *bulkCreateTicketConfig listTicketSchemasConfig *listTicketSchemasConfig getTicketConfig *getTicketConfig - syncDifferConfig *syncDifferConfig syncCompactorConfig *syncCompactorConfig skipFullSync bool storageEngine c1zstore.Engine @@ -819,18 +813,6 @@ func WithKeepPreviousSyncC1ZRuntimeOptIn() Option { } } -func WithDiffSyncs(c1zPath string, baseSyncID string, newSyncID string) Option { - return func(ctx context.Context, cfg *runnerConfig) error { - cfg.onDemand = true - cfg.c1zPath = c1zPath - cfg.syncDifferConfig = &syncDifferConfig{ - baseSyncID: baseSyncID, - appliedSyncID: newSyncID, - } - return nil - } -} - func WithSyncCompactor(outputPath string, filePaths []string, syncIDs []string) Option { return func(ctx context.Context, cfg *runnerConfig) error { cfg.onDemand = true @@ -1079,8 +1061,6 @@ func NewConnectorRunner(ctx context.Context, c types.ConnectorServer, opts ...Op tm = local.NewGetTicket(ctx, cfg.getTicketConfig.ticketID) case cfg.bulkCreateTicketConfig != nil: tm = local.NewBulkTicket(ctx, cfg.bulkCreateTicketConfig.templatePath) - case cfg.syncDifferConfig != nil: - tm = local.NewDiffer(ctx, cfg.c1zPath, cfg.syncDifferConfig.baseSyncID, cfg.syncDifferConfig.appliedSyncID) case cfg.syncCompactorConfig != nil: c := cfg.syncCompactorConfig if len(c.filePaths) != len(c.syncIDs) { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go b/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go index 50fc2e7d..1b081dde 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/connectorstore/connectorstore.go @@ -12,12 +12,10 @@ import ( type SyncType string const ( - SyncTypeFull SyncType = "full" - SyncTypePartial SyncType = "partial" - SyncTypeResourcesOnly SyncType = "resources_only" - SyncTypePartialUpserts SyncType = "partial_upserts" // Diff sync: additions and modifications - SyncTypePartialDeletions SyncType = "partial_deletions" // Diff sync: deletions - SyncTypeAny SyncType = "" + SyncTypeFull SyncType = "full" + SyncTypePartial SyncType = "partial" + SyncTypeResourcesOnly SyncType = "resources_only" + SyncTypeAny SyncType = "" ) var AllSyncTypes = []SyncType{ @@ -25,8 +23,6 @@ var AllSyncTypes = []SyncType{ SyncTypeFull, SyncTypePartial, SyncTypeResourcesOnly, - SyncTypePartialUpserts, - SyncTypePartialDeletions, } // StoreMetadata describes the storage backing a Reader. Returned by diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/crypto/providers/age/age.go b/vendor/github.com/conductorone/baton-sdk/pkg/crypto/providers/age/age.go index 41f8ef16..3ff7d126 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/crypto/providers/age/age.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/crypto/providers/age/age.go @@ -17,6 +17,24 @@ import ( const EncryptionProviderAge = "baton/age/v1" +// KeyIDForRecipient derives the EncryptedData.key_ids entry for an age recipient. +// +// It returns the lowercase hexadecimal SHA-256 digest of the UTF-8 canonical +// recipient string. This is the single source of truth for the key-ID +// derivation convention: producers set EncryptedData.key_ids to this value, and +// consumers that need to correlate ciphertext with recipient key material must +// call this function rather than reimplementing the derivation. Because both the +// SDK and its consumers import this package, the contract is enforced by shared +// code instead of by prose that can drift. +// +// The recipient must be the canonical recipient string (no surrounding +// whitespace, exactly one recipient); callers that accept untrusted input should +// validate it the same way Encrypt does before deriving a key ID. +func KeyIDForRecipient(recipient string) string { + digest := sha256.Sum256([]byte(recipient)) + return hex.EncodeToString(digest[:]) +} + type RecipientEncryptionProvider struct{} func (p *RecipientEncryptionProvider) ValidateConfig(_ context.Context, conf *v2.EncryptionConfig) error { @@ -42,14 +60,13 @@ func (p *RecipientEncryptionProvider) Encrypt(_ context.Context, conf *v2.Encryp return nil, fmt.Errorf("age: failed to finalize encryption: %w", err) } - keyID := sha256.Sum256([]byte(recipientText)) return v2.EncryptedData_builder{ Provider: EncryptionProviderAge, Name: plaintext.GetName(), Description: plaintext.GetDescription(), Schema: plaintext.GetSchema(), EncryptedBytes: ciphertext.Bytes(), - KeyIds: []string{hex.EncodeToString(keyID[:])}, + KeyIds: []string{KeyIDForRecipient(recipientText)}, }.Build(), nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go index d2c55d23..0a0641d5 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file.go @@ -348,7 +348,9 @@ type c1zOptions struct { disableGrantDigestIndex bool // engine is the storage engine to use for newly created files. - // Reads dispatch on magic byte regardless. Default EngineSQLite. + // Reads dispatch on magic byte regardless. NewStore defaults an + // unset engine to EnginePebble; NewC1ZFile (SQLite-only) normalizes + // unset to EngineSQLite. engine c1zstore.Engine // payloadEncoding controls the v3 envelope payload framing. Only @@ -433,8 +435,10 @@ func WithSyncLimit(limit int) C1ZOption { } // WithEngine selects the storage engine for newly created .c1z files. -// Default is EngineSQLite (v1 format). EnginePebble enables the v3 -// engine. +// Under NewStore the default is EnginePebble (v3 format); EngineSQLite +// selects the legacy v1 engine. NewC1ZFile does not share that default: +// it is the SQLite-only constructor, treats an unset engine as +// EngineSQLite, and rejects a writable EnginePebble request. // // Reading existing files dispatches on the file's magic byte and is // independent of this option. diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_attached.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_attached.go index 6a98a6ae..34d37ede 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_attached.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_attached.go @@ -5,13 +5,11 @@ import ( "database/sql" "errors" "fmt" - "time" reader_v2 "github.com/conductorone/baton-sdk/pb/c1/reader/v2" "github.com/conductorone/baton-sdk/pkg/connectorstore" "github.com/conductorone/baton-sdk/pkg/uotel" "github.com/doug-martin/goqu/v9" - "github.com/segmentio/ksuid" ) type C1FileAttached struct { @@ -188,275 +186,3 @@ func (c *C1FileAttached) UpdateSync(ctx context.Context, baseSync *reader_v2.Syn return nil } - -// GenerateSyncDiffFromFile compares the old sync (in attached) with the new sync (in main) -// and generates two new syncs in the main database. -// -// IMPORTANT: This assumes main=NEW/compacted and attached=OLD/base: -// - diffTableFromAttached: items in attached (OLD) not in main (NEW) = deletions -// - diffTableFromMain: items in main (NEW) not in attached (OLD) = upserts (additions) -// -// Parameters: -// - oldSyncID: the sync ID in the attached database (OLD/base state) -// - newSyncID: the sync ID in the main database (NEW/compacted state) -// -// Returns (upsertsSyncID, deletionsSyncID, error). -func (c *C1FileAttached) GenerateSyncDiffFromFile(ctx context.Context, oldSyncID string, newSyncID string) (string, string, error) { - if !c.safe { - return "", "", errors.New("database has been detached") - } - - ctx, span := tracer.Start(ctx, "C1FileAttached.GenerateSyncDiffFromFile") - var err error - defer func() { uotel.EndSpanWithError(span, err) }() - - // Verify both source syncs have been backfilled and support diff before - // generating derived syncs. If they haven't, the expansion columns in - // copied grants may be incomplete. - var oldBackfilled, oldDiff int - err = c.file.rawDb.QueryRowContext(ctx, - fmt.Sprintf("SELECT grants_backfilled, supports_diff FROM attached.%s WHERE sync_id = ?", syncRuns.Name()), - oldSyncID, - ).Scan(&oldBackfilled, &oldDiff) - if err != nil { - return "", "", fmt.Errorf("failed to check old sync %s readiness: %w", oldSyncID, err) - } - if oldBackfilled != 1 { - return "", "", fmt.Errorf("old sync %s has not been backfilled (grants_backfilled=%d)", oldSyncID, oldBackfilled) - } - if oldDiff != 1 { - return "", "", fmt.Errorf("old sync %s does not support diff (supports_diff=%d)", oldSyncID, oldDiff) - } - - var newBackfilled, newDiff int - err = c.file.rawDb.QueryRowContext(ctx, - fmt.Sprintf("SELECT grants_backfilled, supports_diff FROM main.%s WHERE sync_id = ?", syncRuns.Name()), - newSyncID, - ).Scan(&newBackfilled, &newDiff) - if err != nil { - return "", "", fmt.Errorf("failed to check new sync %s readiness: %w", newSyncID, err) - } - if newBackfilled != 1 { - return "", "", fmt.Errorf("new sync %s has not been backfilled (grants_backfilled=%d)", newSyncID, newBackfilled) - } - if newDiff != 1 { - return "", "", fmt.Errorf("new sync %s does not support diff (supports_diff=%d)", newSyncID, newDiff) - } - - // Generate unique IDs for the diff syncs - deletionsSyncID := ksuid.New().String() - upsertsSyncID := ksuid.New().String() - - // Start transaction for atomicity - tx, err := c.file.rawDb.BeginTx(ctx, nil) - if err != nil { - return "", "", fmt.Errorf("failed to begin transaction: %w", err) - } - - // Ensure rollback on error - committed := false - defer func() { - if !committed { - _ = tx.Rollback() - } - }() - - now := time.Now().Format(sqliteTimeFormat) - - // Create the deletions sync first (so upserts is "latest") - // Link it to upserts sync bidirectionally - deletionsInsert := c.file.db.Insert(syncRuns.Name()).Rows(goqu.Record{ - "sync_id": deletionsSyncID, - "started_at": now, - "sync_token": "", - "sync_type": connectorstore.SyncTypePartialDeletions, - "parent_sync_id": oldSyncID, - "linked_sync_id": upsertsSyncID, - "supports_diff": 1, - "grants_backfilled": 1, - }) - query, args, err := deletionsInsert.ToSQL() - if err != nil { - return "", "", fmt.Errorf("failed to build deletions sync insert: %w", err) - } - if _, err = tx.ExecContext(ctx, query, args...); err != nil { - return "", "", fmt.Errorf("failed to create deletions sync: %w", err) - } - - // Create the upserts sync, linked to deletions sync - upsertsInsert := c.file.db.Insert(syncRuns.Name()).Rows(goqu.Record{ - "sync_id": upsertsSyncID, - "started_at": now, - "sync_token": "", - "sync_type": connectorstore.SyncTypePartialUpserts, - "parent_sync_id": oldSyncID, - "linked_sync_id": deletionsSyncID, - "supports_diff": 1, - "grants_backfilled": 1, - }) - query, args, err = upsertsInsert.ToSQL() - if err != nil { - return "", "", fmt.Errorf("failed to build upserts sync insert: %w", err) - } - if _, err = tx.ExecContext(ctx, query, args...); err != nil { - return "", "", fmt.Errorf("failed to create upserts sync: %w", err) - } - - // Process each table - // main=NEW, attached=OLD - // - diffTableFromAttachedTx finds items in OLD not in NEW = deletions - // - diffTableFromMainTx finds items in NEW not in OLD or modified = upserts - tables := []string{"v1_resource_types", "v1_resources", "v1_entitlements", "v1_grants"} - for _, tableName := range tables { - if err := c.diffTableFromAttachedTx(ctx, tx, tableName, oldSyncID, newSyncID, deletionsSyncID); err != nil { - return "", "", fmt.Errorf("failed to generate deletions for %s: %w", tableName, err) - } - if err := c.diffTableFromMainTx(ctx, tx, tableName, oldSyncID, newSyncID, upsertsSyncID); err != nil { - return "", "", fmt.Errorf("failed to generate upserts for %s: %w", tableName, err) - } - } - - // End the syncs (deletions first, then upserts) - endedAt := time.Now().Format(sqliteTimeFormat) - - endDeletions := c.file.db.Update(syncRuns.Name()). - Set(goqu.Record{"ended_at": endedAt}). - Where(goqu.C("sync_id").Eq(deletionsSyncID), goqu.C("ended_at").IsNull()) - query, args, err = endDeletions.ToSQL() - if err != nil { - return "", "", fmt.Errorf("failed to build end deletions sync: %w", err) - } - if _, err = tx.ExecContext(ctx, query, args...); err != nil { - return "", "", fmt.Errorf("failed to end deletions sync: %w", err) - } - - endUpserts := c.file.db.Update(syncRuns.Name()). - Set(goqu.Record{"ended_at": endedAt}). - Where(goqu.C("sync_id").Eq(upsertsSyncID), goqu.C("ended_at").IsNull()) - query, args, err = endUpserts.ToSQL() - if err != nil { - return "", "", fmt.Errorf("failed to build end upserts sync: %w", err) - } - if _, err = tx.ExecContext(ctx, query, args...); err != nil { - return "", "", fmt.Errorf("failed to end upserts sync: %w", err) - } - - // Commit transaction - if err = tx.Commit(); err != nil { - return "", "", fmt.Errorf("failed to commit transaction: %w", err) - } - committed = true - c.file.dbUpdated.Store(true) - - return upsertsSyncID, deletionsSyncID, nil -} - -// diffTableFromAttachedTx finds items in attached (OLD) that don't exist in main (NEW). -// These are DELETIONS - items that existed before but no longer exist. -// Uses the provided transaction. -func (c *C1FileAttached) diffTableFromAttachedTx(ctx context.Context, tx *sql.Tx, tableName string, oldSyncID string, newSyncID string, targetSyncID string) error { - columns, err := c.getTableColumns(ctx, tx, tableName) - if err != nil { - return err - } - - // Build column lists - columnList := "" - selectList := "" - for i, col := range columns { - if i > 0 { - columnList += ", " - selectList += ", " - } - qcol := quoteIdentifier(col) - columnList += qcol - if col == "sync_id" { - selectList += "? as " + qcol - } else { - selectList += qcol - } - } - - // Insert items from attached (OLD) that don't exist in main (NEW) - // oldSyncID is in attached, newSyncID is in main - //nolint:gosec // table names are from hardcoded list; column names are validated - query := fmt.Sprintf(` - INSERT INTO main.%s (%s) - SELECT %s - FROM attached.%s AS a - WHERE a.sync_id = ? - AND NOT EXISTS ( - SELECT 1 FROM main.%s AS m - WHERE m.external_id = a.external_id AND m.sync_id = ? - ) - `, tableName, columnList, selectList, tableName, tableName) - - _, err = tx.ExecContext(ctx, query, targetSyncID, oldSyncID, newSyncID) - return err -} - -// diffTableFromMainTx finds items in main (NEW) that are new or modified compared to attached (OLD). -// These are UPSERTS - items that are new or have changed. -// Uses the provided transaction. -func (c *C1FileAttached) diffTableFromMainTx(ctx context.Context, tx *sql.Tx, tableName string, oldSyncID string, newSyncID string, targetSyncID string) error { - columns, err := c.getTableColumns(ctx, tx, tableName) - if err != nil { - return err - } - - // Build column lists - columnList := "" - selectList := "" - for i, col := range columns { - if i > 0 { - columnList += ", " - selectList += ", " - } - qcol := quoteIdentifier(col) - columnList += qcol - if col == "sync_id" { - selectList += "? as " + qcol - } else { - selectList += qcol - } - } - - // Insert items from main (NEW) that are: - // 1. Not in attached (OLD) - additions - // 2. In attached but with different data - modifications - // newSyncID is in main, oldSyncID is in attached - // - // For grants, we also compare the expansion column since GrantExpandable - // annotation is stored separately from data. - var dataCompare string - if tableName == grants.Name() { - // For grants: compare both data AND expansion columns. - // Use IFNULL to handle NULL expansion values. - dataCompare = "(a.data != m.data OR IFNULL(a.expansion, X'') != IFNULL(m.expansion, X''))" - } else { - dataCompare = "a.data != m.data" - } - - //nolint:gosec // table names are from hardcoded list; column names are validated - query := fmt.Sprintf(` - INSERT INTO main.%s (%s) - SELECT %s - FROM main.%s AS m - WHERE m.sync_id = ? - AND ( - NOT EXISTS ( - SELECT 1 FROM attached.%s AS a - WHERE a.external_id = m.external_id AND a.sync_id = ? - ) - OR EXISTS ( - SELECT 1 FROM attached.%s AS a - WHERE a.external_id = m.external_id - AND a.sync_id = ? - AND %s - ) - ) - `, tableName, columnList, selectList, tableName, tableName, tableName, dataCompare) - - _, err = tx.ExecContext(ctx, query, targetSyncID, newSyncID, oldSyncID, oldSyncID) - return err -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_store.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_store.go index bc5d5c14..7e1281c6 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_store.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1file_store.go @@ -299,7 +299,7 @@ func (s c1FileSyncMeta) LatestFullSync(ctx context.Context) (*c1zstore.SyncRun, } // LatestFinishedSyncOfAnyType implements SyncMeta. Returns the most-recent -// finished sync of any type (including diff types), or nil if none. +// finished sync of any type, or nil if none. func (s c1FileSyncMeta) LatestFinishedSyncOfAnyType(ctx context.Context) (*c1zstore.SyncRun, error) { run, err := s.c.getFinishedSync(ctx, 0, connectorstore.SyncTypeAny) if err != nil { @@ -354,11 +354,6 @@ func (f c1FileFileOps) CopyIsolateSync(ctx context.Context, outPath string, sync return f.c.CopyIsolateSync(ctx, outPath, syncID, c1fOpts...) } -// GenerateSyncDiff implements FileOps. Direct passthrough. -func (f c1FileFileOps) GenerateSyncDiff(ctx context.Context, baseSyncID, appliedSyncID string) (string, error) { - return f.c.GenerateSyncDiff(ctx, baseSyncID, appliedSyncID) -} - type c1FileSessionStore struct{ c *C1File } func (s c1FileSessionStore) Get(ctx context.Context, key string, opt ...sessions.SessionStoreOption) ([]byte, bool, error) { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/c1zstore.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/c1zstore.go index 8aa50210..3a5f51b5 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/c1zstore.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/c1zstore.go @@ -28,7 +28,7 @@ import ( // // - Grant operations with expansion-aware semantics, accessed via Grants(). // - Sync-run metadata operations, accessed via SyncMeta(). -// - File-level operations (clone, diff), accessed via FileOps(). +// - File-level operations (clone), accessed via FileOps(). // // Implementations: // @@ -61,3 +61,17 @@ type Store interface { SessionStore() sessions.SessionStore } + +// GrantGenerationDigest binds derived metadata to the exact grant generation +// stored in an artifact. +type GrantGenerationDigest struct { + Hash []byte + Count int64 + ABIVersion uint32 +} + +// GrantGenerationDigestReader is implemented by stores that persist an exact +// whole-file grant digest at seal time. +type GrantGenerationDigestReader interface { + GrantGenerationDigest(ctx context.Context) (GrantGenerationDigest, bool, error) +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/cleanup_policy.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/cleanup_policy.go index 646cabeb..3f86d3a8 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/cleanup_policy.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/cleanup_policy.go @@ -22,9 +22,9 @@ const defaultCleanupSyncLimit = 2 // considered "in flight" and must never be pruned). // - currentSyncID is skipped when non-empty (the actively-open sync // is also off-limits). -// - Candidates are bucketed by Type into fullSyncs, partials, and -// diff syncs. SyncTypeFull and any unrecognized type go into -// fullSyncs (matches the SQLite default branch). +// - Candidates are bucketed by Type into fullSyncs and partials. +// SyncTypeFull and any unrecognized type go into fullSyncs +// (matches the SQLite default branch). // - syncLimit is the number of *additional* full syncs to retain // beyond the current one. The caller has already decremented for // a running sync (see ResolveCleanupSyncLimit), so this function @@ -32,9 +32,6 @@ const defaultCleanupSyncLimit = 2 // oldest overflow is selected for deletion. // - Once the earliest-kept full sync is established, partials that // ended before that sync started are selected for deletion. -// - When more than two diff syncs (partial_upserts / partial_deletions) -// exist, only the most recent diff sync and its linked pair are -// retained; everything else is selected. // // Order matters: callers must pass candidates in oldest-first order // so "drop the oldest overflow" trims the right end. SQLite supplies @@ -44,7 +41,6 @@ const defaultCleanupSyncLimit = 2 func SelectSyncsToDelete(candidates []SyncRun, currentSyncID string, syncLimit int) []string { var fullSyncs []SyncRun var partials []SyncRun - var diffSyncs []SyncRun for _, sr := range candidates { if sr.EndedAt == nil || sr.ID == currentSyncID { @@ -53,8 +49,6 @@ func SelectSyncsToDelete(candidates []SyncRun, currentSyncID string, syncLimit i switch sr.Type { case connectorstore.SyncTypePartial, connectorstore.SyncTypeResourcesOnly: partials = append(partials, sr) - case connectorstore.SyncTypePartialUpserts, connectorstore.SyncTypePartialDeletions: - diffSyncs = append(diffSyncs, sr) default: fullSyncs = append(fullSyncs, sr) } @@ -86,31 +80,6 @@ func SelectSyncsToDelete(candidates []SyncRun, currentSyncID string, syncLimit i } } - // Diff syncs: keep latest + its linked partner; drop the rest. - // Mirrors the SQLite branch at sync_runs.go:884-931. The - // "diffSyncs > 2" guard preserves the historical no-op behavior - // for small histories — we don't prune until there's enough to - // be worth touching. - if len(diffSyncs) > 2 { - syncByID := make(map[string]SyncRun, len(diffSyncs)) - for _, ds := range diffSyncs { - syncByID[ds.ID] = ds - } - latestDiff := diffSyncs[len(diffSyncs)-1] - keepIDs := map[string]struct{}{latestDiff.ID: {}} - if latestDiff.LinkedSyncID != "" { - if _, ok := syncByID[latestDiff.LinkedSyncID]; ok { - keepIDs[latestDiff.LinkedSyncID] = struct{}{} - } - } - for _, ds := range diffSyncs { - if _, keep := keepIDs[ds.ID]; keep { - continue - } - toDelete = append(toDelete, ds.ID) - } - } - return toDelete } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/engine.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/engine.go index 833fa7cd..9fb8a684 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/engine.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/engine.go @@ -13,12 +13,15 @@ import ( type Engine string const ( - // EngineSQLite is the default engine: the v1 .c1z format backed by - // a zstd-compressed SQLite database. Connectors use this; backend - // infra can opt out. + // EngineSQLite is the legacy v1 engine: the v1 .c1z format backed by + // a zstd-compressed SQLite database. Callers opt into it via + // WithEngine; the NewStore default is EnginePebble. (The SQLite-only + // NewC1ZFile constructor is the exception: it treats an unset engine + // as EngineSQLite.) EngineSQLite Engine = "sqlite" - // EnginePebble is the v3 engine: a Pebble LSM wrapped in the v3 + // EnginePebble is the v3 engine and the NewStore default when + // callers do not specify one: a Pebble LSM wrapped in the v3 // envelope. This is the in-process identity AND the value callers // select with (the --storage-engine flag and the gRPC sync-task // field both pass "pebble"); it must stay "pebble" for those diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/file_ops.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/file_ops.go index dd219b7b..9bee5546 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/file_ops.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/file_ops.go @@ -18,11 +18,6 @@ type FileOps interface { // the target sync row-by-row. It is the optimized isolation step for large // files; the output contains only the target sync and is schema-normalized. CopyIsolateSync(ctx context.Context, outPath string, syncID string, opts ...CloneSyncOption) error - - // GenerateSyncDiff computes the diff between two existing sync runs - // in this same file and writes the delta as a new SyncTypePartial - // sync. Returns the new sync's id. Used by the local differ CLI. - GenerateSyncDiff(ctx context.Context, baseSyncID, appliedSyncID string) (diffSyncID string, err error) } // CloneSyncOptions carries the engine-neutral knobs for FileOps.CloneSync. diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/sync_meta.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/sync_meta.go index 1eb1d5c3..7e7f2d68 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/sync_meta.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore/sync_meta.go @@ -15,9 +15,10 @@ import ( // All methods are callable without an active sync. type SyncMeta interface { // MarkSyncSupportsDiff sets the supports_diff flag on the given sync. - // Called by pkg/sync.parallelSyncer after graph construction to signal - // that the sync run has SQL-layer grant metadata populated and diff - // consumers may rely on it. + // Called by pkg/sync.parallelSyncer when data collection completes to + // signal that the sync run has SQL-layer grant metadata populated. + // The name is historical (the marker once gated diff-sync generation); + // today it gates `baton rollback-expansion`. MarkSyncSupportsDiff(ctx context.Context, syncID string) error // LatestFullSync returns the most-recently-finished SyncTypeFull sync @@ -25,9 +26,8 @@ type SyncMeta interface { LatestFullSync(ctx context.Context) (*SyncRun, error) // LatestFinishedSyncOfAnyType returns the most-recently-finished sync - // of any type (including diff types), or nil if none exists. Used by - // tooling that wants to inspect whatever sync finished last regardless - // of type. + // of any type, or nil if none exists. Used by tooling that wants to + // inspect whatever sync finished last regardless of type. LatestFinishedSyncOfAnyType(ctx context.Context) (*SyncRun, error) // Stats returns a map of table-name to row-count for the given sync. @@ -66,8 +66,7 @@ type IngestInvariantVerificationWriter interface { // sync_runs schema. // // Callers typically only read ID, Type, and the timestamps; the rest is -// included for completeness and for use by tooling (e.g. sync-diff -// pipelines need ParentSyncID and LinkedSyncID). +// included for completeness and for use by tooling. type SyncRun struct { ID string StartedAt *time.Time @@ -75,7 +74,6 @@ type SyncRun struct { SyncToken string Type connectorstore.SyncType ParentSyncID string - LinkedSyncID string SupportsDiff bool Stats *reader_v2.SyncStats IngestInvariantVerification diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/diff.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/diff.go deleted file mode 100644 index c991b585..00000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/diff.go +++ /dev/null @@ -1,124 +0,0 @@ -package dotc1z - -import ( - "context" - "fmt" - "strings" - - "github.com/conductorone/baton-sdk/pkg/connectorstore" - "github.com/doug-martin/goqu/v9" - "github.com/segmentio/ksuid" -) - -func (c *C1File) GenerateSyncDiff(ctx context.Context, baseSyncID string, appliedSyncID string) (string, error) { - if c.readOnly { - return "", ErrReadOnly - } - - // Validate that both sync runs exist - baseSync, err := c.getSync(ctx, baseSyncID) - if err != nil { - return "", err - } - if baseSync == nil { - return "", fmt.Errorf("generate-diff: base sync not found") - } - - newSync, err := c.getSync(ctx, appliedSyncID) - if err != nil { - return "", err - } - if newSync == nil { - return "", fmt.Errorf("generate-diff: new sync not found") - } - - // Generate a new unique ID for the diff sync - diffSyncID := ksuid.New().String() - - if err := c.insertSyncRun(ctx, diffSyncID, connectorstore.SyncTypePartial, baseSyncID); err != nil { - return "", err - } - - for _, t := range allTableDescriptors { - if strings.Contains(t.Name(), syncRunsTableName) { - continue - } - - q, args, err := c.diffTableQuery(t, baseSyncID, appliedSyncID, diffSyncID) - if err != nil { - return "", err - } - if q == "" { - continue - } - _, err = c.db.ExecContext(ctx, q, args...) - if err != nil { - return "", err - } - c.dbUpdated.Store(true) - } - - if err := c.endSyncRun(ctx, diffSyncID); err != nil { - return "", err - } - - return diffSyncID, nil -} - -func (c *C1File) diffTableQuery(table tableDescriptor, baseSyncID, appliedSyncID, newSyncID string) (string, []any, error) { - // Define the columns to select based on the table name - columns := []interface{}{ - "external_id", - "data", - "sync_id", - "discovered_at", - } - - tableName := table.Name() - // Add table-specific columns - switch { - case strings.Contains(tableName, sessionStoreTableName): - // caching is not relevant to diffs. - return "", nil, nil - case strings.Contains(tableName, resourcesTableName): - columns = append(columns, "resource_type_id", "parent_resource_type_id", "parent_resource_id") - case strings.Contains(tableName, resourceTypesTableName): - // Nothing new to add here - case strings.Contains(tableName, grantsTableName): - columns = append(columns, "resource_type_id", "resource_id", "entitlement_id", "principal_resource_type_id", "principal_resource_id") - case strings.Contains(tableName, entitlementsTableName): - columns = append(columns, "resource_type_id", "resource_id") - case strings.Contains(tableName, assetsTableName): - columns = append(columns, "content_type") - } - - // Build the subquery to find external_ids in the base sync - subquery := c.db.Select("external_id"). - From(tableName). - Where(goqu.C("sync_id").Eq(baseSyncID)) - - queryColumns := []interface{}{} - for _, col := range columns { - if col == "sync_id" { //nolint:goconst,nolintlint // ... - queryColumns = append(queryColumns, goqu.L(fmt.Sprintf("'%s' as sync_id", newSyncID))) - continue - } - queryColumns = append(queryColumns, col) - } - - // Build the main query to select records from newSyncID that don't exist in baseSyncID - query := c.db.Insert(tableName). - Cols(columns...). - Prepared(true). - FromQuery( - c.db.Select(queryColumns...). - From(tableName). - Where( - goqu.C("sync_id").Eq(appliedSyncID), - goqu.C("external_id").NotIn(subquery), - ), - ) - - // Generate the SQL and args - return query.ToSQL() -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter.go index 23048684..e4ddfc06 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter.go @@ -1041,10 +1041,6 @@ func v2SyncTypeToV3(t connectorstore.SyncType) v3.SyncType { return v3.SyncType_SYNC_TYPE_PARTIAL case connectorstore.SyncTypeResourcesOnly: return v3.SyncType_SYNC_TYPE_RESOURCES_ONLY - case connectorstore.SyncTypePartialUpserts: - return v3.SyncType_SYNC_TYPE_PARTIAL_UPSERTS - case connectorstore.SyncTypePartialDeletions: - return v3.SyncType_SYNC_TYPE_PARTIAL_DELETIONS default: return v3.SyncType_SYNC_TYPE_UNSPECIFIED } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_diff.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_diff.go deleted file mode 100644 index 26b81937..00000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_diff.go +++ /dev/null @@ -1,21 +0,0 @@ -package pebble - -import ( - "context" - "errors" -) - -// ErrDiffUnsupported is returned by the Pebble v3 engine's -// GenerateSyncDiff. A v3 c1z holds exactly one sync by contract, so the -// precondition GenerateSyncDiff needs — two ended syncs (base + applied) -// co-resident in one file — can never be satisfied. Diffs must be -// computed a layer up, across two separate c1z files. -var ErrDiffUnsupported = errors.New("pebble v3 engine: GenerateSyncDiff is unsupported (single-sync contract)") - -// generateSyncDiff is unsupported on the single-sync Pebble engine; see -// ErrDiffUnsupported. The previous additions-only set-difference -// implementation was removed when the keyspace dropped its sync_id -// region (a second sync can no longer coexist with the base). -func generateSyncDiff(_ context.Context, _ *Adapter, _, _ string) (string, error) { - return "", ErrDiffUnsupported -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_file_ops.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_file_ops.go index 4e2bf73a..cdef69f9 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_file_ops.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_file_ops.go @@ -8,9 +8,7 @@ import ( // FileOps returns the FileOps sub-store backed by the Pebble // adapter. Implements c1zstore.Store.FileOps(). CloneSync materializes -// the single sync's data into a fresh c1z (used by `baton clone`); -// GenerateSyncDiff is unsupported (single-sync contract — see -// ErrDiffUnsupported). +// the single sync's data into a fresh c1z (used by `baton clone`). func (e *Engine) FileOps() c1zstore.FileOps { return pebbleFileOps{e: e, encoding: c1zstore.PayloadEncodingTarZstd} } @@ -45,10 +43,3 @@ func (f pebbleFileOps) CloneSync(ctx context.Context, outPath string, syncID str func (f pebbleFileOps) CopyIsolateSync(ctx context.Context, outPath string, syncID string, opts ...c1zstore.CloneSyncOption) error { return cloneSync(ctx, f.e, f.encoding, outPath, syncID, opts...) } - -// GenerateSyncDiff is unsupported on the Pebble v3 engine — a c1z -// holds exactly one sync by contract, so base + applied syncs can't be -// co-resident in one file. Always returns ErrDiffUnsupported. -func (f pebbleFileOps) GenerateSyncDiff(ctx context.Context, baseSyncID, appliedSyncID string) (string, error) { - return generateSyncDiff(ctx, f.e, baseSyncID, appliedSyncID) -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_grants_store.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_grants_store.go index de414443..9a2fd703 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_grants_store.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_grants_store.go @@ -17,7 +17,7 @@ import ( // Grants returns the GrantStore implementation backed by the Pebble // adapter. Implements c1zstore.Store.Grants(); used by the -// expander, the c1-side fileClientWrapper, and the differ. +// expander and the c1-side fileClientWrapper. // // needs_expansion is populated at PutGrants time: V2GrantToV3 extracts // the GrantExpandable annotation and sets NeedsExpansion, which keys the diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_sync_meta.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_sync_meta.go index d7ec808e..b820d6bf 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_sync_meta.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/adapter_sync_meta.go @@ -31,12 +31,9 @@ var _ c1zstore.IngestInvariantVerificationWriter = pebbleSyncMeta{} // MarkSyncSupportsDiff sets supports_diff = true on the named sync's // run record. Used by pkg/sync.parallelSyncer after graph // construction to signal that the sync has SQL-layer grant metadata -// populated and diff consumers may rely on it. -// -// FileOps().GenerateSyncDiff and FileOps().CloneSync are implemented -// on the Pebble adapter (see adapter_diff.go and -// adapter_clone_sync.go); the bit stored here gates downstream -// consumers' willingness to call them, in parity with the SQLite +// populated. The name is historical (the marker once gated diff-sync +// generation, since removed); today it gates `baton +// rollback-expansion`, in parity with the SQLite // sync_runs.supports_diff column. func (s pebbleSyncMeta) MarkSyncSupportsDiff(ctx context.Context, syncID string) error { if syncID == "" { @@ -178,7 +175,6 @@ func syncRunRecordToExported(r *v3.SyncRunRecord) *c1zstore.SyncRun { Type: syncTypeV3ToConnectorstore(r.GetType()), SyncToken: r.GetSyncToken(), ParentSyncID: r.GetParentSyncId(), - LinkedSyncID: r.GetLinkedSyncId(), SupportsDiff: r.GetSupportsDiff(), IngestInvariantVerification: verification, } @@ -222,7 +218,6 @@ func (e *Engine) sortedSyncRuns(ctx context.Context) ([]c1zstore.SyncRun, error) SyncToken: r.GetSyncToken(), ParentSyncID: r.GetParentSyncId(), SupportsDiff: r.GetSupportsDiff(), - LinkedSyncID: r.GetLinkedSyncId(), IngestInvariantVerification: verification, } if t := r.GetStartedAt(); t != nil { @@ -277,8 +272,8 @@ func (e *Engine) CleanupCandidates(ctx context.Context) ([]c1zstore.SyncRun, err // returned on the first call and the next-page token is always empty. // pageToken and pageSize are accepted for parity with the SQLite // ListSyncRuns and are not used. It backs the c1z sanitizer's -// source-side sync-graph-metadata read (linked_sync_id, supports_diff), -// which the gRPC reader surface does not carry. +// source-side sync-run-metadata read (supports_diff), which the gRPC +// reader surface does not carry. func (e *Engine) ListSyncRuns(ctx context.Context, pageToken string, pageSize uint32) ([]*c1zstore.SyncRun, string, error) { runs, err := e.sortedSyncRuns(ctx) if err != nil { @@ -304,10 +299,6 @@ func syncTypeV3ToConnectorstore(t v3.SyncType) connectorstore.SyncType { return connectorstore.SyncTypePartial case v3.SyncType_SYNC_TYPE_RESOURCES_ONLY: return connectorstore.SyncTypeResourcesOnly - case v3.SyncType_SYNC_TYPE_PARTIAL_UPSERTS: - return connectorstore.SyncTypePartialUpserts - case v3.SyncType_SYNC_TYPE_PARTIAL_DELETIONS: - return connectorstore.SyncTypePartialDeletions default: return "" } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/cleanup.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/cleanup.go index 416c29b5..057c08f6 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/cleanup.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/cleanup.go @@ -41,6 +41,8 @@ func scopedRanges() [][2][]byte { // Stats sidecar — single key; the half-open range shape // contains exactly that one key. {encodeSyncStatsKey(), upperBoundOf(encodeSyncStatsKey())}, + // Entitlement-graph sidecar — same single-key shape. + {EntitlementGraphSidecarLowerBound(), EntitlementGraphSidecarUpperBound()}, } } @@ -86,6 +88,7 @@ func (e *Engine) ResetForNewSync(ctx context.Context) error { spans := []pebble.KeyRange{ {Start: []byte{versionV3, typeResourceType}, End: []byte{versionV3, typeEngineMeta}}, {Start: SyncStatsSidecarLowerBound(), End: SyncStatsSidecarUpperBound()}, + {Start: EntitlementGraphSidecarLowerBound(), End: EntitlementGraphSidecarUpperBound()}, } // AllowSealed: StartNewSync legitimately replaces a finished (sealed) // sync; the wipe is the first step of leaving the sealed state. The diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/engine.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/engine.go index 6e702cf0..adcf0a82 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/engine.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/engine.go @@ -170,8 +170,8 @@ type Engine struct { // save/close (checkpoint + envelope encode) never benefits from either. // Binding a sync again (SetCurrentSync / MarkFreshSync) unseals and // resumes compactions. Sync-run metadata writes (PutSyncRunRecord and - // friends) are exempt — callers legitimately stamp ended_at overrides, - // diff links, and supports_diff markers on a finished sync. Without this + // friends) are exempt — callers legitimately stamp ended_at overrides + // and supports_diff markers on a finished sync. Without this // state the "no writes while compactions are paused" invariant was // convention only, and a caller that kept writing after EndSync would // silently accumulate L0 until pebble stalled writes at @@ -639,8 +639,8 @@ func (e *Engine) withWrite(fn func() error) error { // withWriteAllowSealed is withWrite without the sealed check. Reserved for // writes that are part of the sealed lifecycle itself: sync-run metadata -// stamps on a finished sync (ended_at overrides, diff links, supports_diff) -// and ResetForNewSync's wipe on the way into a new sync. Record-data writes +// stamps on a finished sync (ended_at overrides, supports_diff) and +// ResetForNewSync's wipe on the way into a new sync. Record-data writes // must use withWrite. func (e *Engine) withWriteAllowSealed(fn func() error) error { if err := e.checkWritableAllowSealed(); err != nil { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/entitlement_graph_sidecar.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/entitlement_graph_sidecar.go new file mode 100644 index 00000000..75ad75b7 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/entitlement_graph_sidecar.go @@ -0,0 +1,88 @@ +package pebble + +import ( + "context" + "errors" + + "github.com/cockroachdb/pebble/v2" + + "github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/codec" +) + +// Entitlement-graph sidecar: an opaque blob (owned by pkg/sync/expand) +// holding the sync's expansion graph, so it rides the c1z instead of +// bloating the sync token. Same single-fixed-key shape as the stats +// sidecar; absent on files written by a pre-sidecar SDK. + +// encodeEntitlementGraphKey returns the engine-meta key for the single +// sync's graph blob. One sync per file, so no sync_id in the key. +func encodeEntitlementGraphKey() []byte { + buf := make([]byte, 0, 6+len("entitlement-graph")) + buf = append(buf, versionV3, typeEngineMeta) + buf = codec.AppendTupleString(buf, "entitlement-graph") + buf = codec.AppendTupleSeparator(buf) + return buf +} + +// EntitlementGraphSidecarLowerBound / UpperBound expose the sidecar's +// single-key range for cleanup and compaction. +func EntitlementGraphSidecarLowerBound() []byte { + return encodeEntitlementGraphKey() +} + +func EntitlementGraphSidecarUpperBound() []byte { + return upperBoundOf(EntitlementGraphSidecarLowerBound()) +} + +// PutEntitlementGraphSidecar stores the opaque graph blob. Same write +// barrier as the stats sidecar: callers span EndSync's sealed window. +func (e *Engine) PutEntitlementGraphSidecar(ctx context.Context, data []byte) error { + if err := ctx.Err(); err != nil { + return err + } + return e.withWriteAllowSealed(func() error { + // Re-check after waiting for the engine's write lock. The context may + // have been canceled while another writer held the lock. + if err := ctx.Err(); err != nil { + return err + } + return e.db.MetaSet(encodeEntitlementGraphKey(), data, pebble.Sync) + }) +} + +// GetEntitlementGraphSidecar returns the stored blob, or (nil, nil) if +// none exists. +func (e *Engine) GetEntitlementGraphSidecar(ctx context.Context) ([]byte, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + val, closer, err := e.db.Get(encodeEntitlementGraphKey()) + if err != nil { + if errors.Is(err, pebble.ErrNotFound) { + return nil, nil + } + return nil, err + } + defer closer.Close() + out := make([]byte, len(val)) + copy(out, val) + if err := ctx.Err(); err != nil { + return nil, err + } + return out, nil +} + +// DeleteEntitlementGraphSidecar removes the blob (no-op when absent). +func (e *Engine) DeleteEntitlementGraphSidecar(ctx context.Context) error { + if err := ctx.Err(); err != nil { + return err + } + return e.withWriteAllowSealed(func() error { + // Re-check after waiting for the engine's write lock. The context may + // have been canceled while another writer held the lock. + if err := ctx.Err(); err != nil { + return err + } + return e.db.MetaDelete(encodeEntitlementGraphKey(), pebble.Sync) + }) +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/if_newer.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/if_newer.go deleted file mode 100644 index bf7902bf..00000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/if_newer.go +++ /dev/null @@ -1,293 +0,0 @@ -package pebble - -import ( - "context" - "errors" - "fmt" - - "github.com/cockroachdb/pebble/v2" - "google.golang.org/protobuf/types/known/timestamppb" - - v3 "github.com/conductorone/baton-sdk/pb/c1/storage/v3" -) - -// *IfNewer upsert methods. Mirror the SQLite engine's -// PutGrantsIfNewer / PutResourcesIfNewer / PutEntitlementsIfNewer / -// PutResourceTypesIfNewer semantics: only overwrite the existing -// record when the incoming record's discovered_at is strictly newer. -// -// Used by partial-sync workflows (SyncTypePartialUpserts / -// SyncTypePartialDeletions) where a connector replays a recent -// window of changes and must not regress an existing record's -// discovered_at to an older timestamp. -// -// Mechanism: for each candidate record we read the existing record -// (if any), compare discovered_at, and decide. Records that pass the -// freshness check go into a single batch and commit once. The -// fresh-sync write path is disabled here — *IfNewer is by definition -// not a fresh sync (we're filtering against existing data). - -// PutGrantRecordsIfNewer writes records that are strictly newer than -// the stored copy. Records without a discovered_at are treated as -// "always write" (caller is asserting freshness explicitly). -func (e *Engine) PutGrantRecordsIfNewer(ctx context.Context, records ...*v3.GrantRecord) error { - if len(records) == 0 { - return nil - } - return e.withWrite(func() error { - if err := e.requireCurrentSync(); err != nil { - return err - } - batch := e.db.NewRecordBatch() - defer batch.Close() - // No inline hash-index or digest maintenance here: both are - // derived at seal time (the fused deferred pass). But IfNewer is - // the partial-sync path — it mutates a CLONED sealed file whose - // digests are built — so StageGrantPutInline's derivers stage - // the touched entitlements' digest invalidation whenever - // digests are present (StageGrantDigestInvalidation deriver, - // records.go). - written := 0 - for _, r := range records { - if r == nil { - continue - } - id, err := grantIdentityFromRecord(r) - if err != nil { - return err - } - key := encodeGrantIdentityKey(id) - hadOld := false - oldVal, closer, getErr := e.db.Get(key) - switch { - case getErr == nil: - write, err := discoveredAtIsNewerThanRaw(r.GetDiscoveredAt(), oldVal, grantDiscoveredAtField) - if err != nil { - closer.Close() - return fmt.Errorf("PutGrantRecordsIfNewer: scan old discovered_at: %w", err) - } - if !write { - closer.Close() - continue - } - hadOld = true - closer.Close() - case errors.Is(getErr, pebble.ErrNotFound): - // no existing record — write unconditionally - default: - return fmt.Errorf("PutGrantRecordsIfNewer: get: %w", getErr) - } - val, err := marshalRecord(r) - if err != nil { - return err - } - // Inline regime: the typed op stages the row plus prior-row - // index cleanup, both index entries, and digest invalidation - // (this IS the partial-sync path the invalidation exists for). - if err := batch.StageGrantPutInline(key, val, hadOld, r.GetNeedsExpansion()); err != nil { - return err - } - written++ - } - if written == 0 { - return nil - } - return batch.Commit(writeOpts(e.opts.durability)) - }) -} - -// PutResourceRecordsIfNewer writes resources only when the incoming -// discovered_at is strictly newer than the stored copy. -func (e *Engine) PutResourceRecordsIfNewer(ctx context.Context, records ...*v3.ResourceRecord) error { - if len(records) == 0 { - return nil - } - return e.withWrite(func() error { - if err := e.requireCurrentSync(); err != nil { - return err - } - batch := e.db.NewRecordBatch() - defer batch.Close() - written := 0 - for _, r := range records { - if r == nil { - continue - } - key := encodeResourceKey(r.GetResourceTypeId(), r.GetResourceId()) - oldVal, closer, getErr := e.db.Get(key) - switch { - case getErr == nil: - write, err := discoveredAtIsNewerThanRaw(r.GetDiscoveredAt(), oldVal, resourceDiscoveredAtField) - if err != nil { - closer.Close() - return fmt.Errorf("PutResourceRecordsIfNewer: scan old discovered_at: %w", err) - } - if !write { - closer.Close() - continue - } - val, err := marshalRecord(r) - if err != nil { - closer.Close() - return err - } - // Typed op consumes the prior value for by_parent cleanup. - err = batch.StageResourcePut(key, val, oldVal, r.GetResourceTypeId(), r.GetResourceId()) - closer.Close() - if err != nil { - return err - } - written++ - continue - case errors.Is(getErr, pebble.ErrNotFound): - default: - return fmt.Errorf("PutResourceRecordsIfNewer: get: %w", getErr) - } - val, err := marshalRecord(r) - if err != nil { - return err - } - if err := batch.StageResourcePut(key, val, nil, r.GetResourceTypeId(), r.GetResourceId()); err != nil { - return err - } - written++ - } - if written == 0 { - return nil - } - return batch.Commit(writeOpts(e.opts.durability)) - }) -} - -// PutEntitlementRecordsIfNewer writes entitlements only when newer. -func (e *Engine) PutEntitlementRecordsIfNewer(ctx context.Context, records ...*v3.EntitlementRecord) error { - if len(records) == 0 { - return nil - } - return e.withWrite(func() error { - if err := e.requireCurrentSync(); err != nil { - return err - } - batch := e.db.NewRecordBatch() - defer batch.Close() - written := 0 - for _, r := range records { - if r == nil { - continue - } - id, err := entitlementIdentityFromRecord(r) - if err != nil { - return err - } - key := encodeEntitlementIdentityKey(id) - oldVal, closer, getErr := e.db.Get(key) - switch { - case getErr == nil: - write, err := discoveredAtIsNewerThanRaw(r.GetDiscoveredAt(), oldVal, entitlementDiscoveredAtField) - if err != nil { - closer.Close() - return fmt.Errorf("PutEntitlementRecordsIfNewer: scan old discovered_at: %w", err) - } - if !write { - closer.Close() - continue - } - closer.Close() - case errors.Is(getErr, pebble.ErrNotFound): - default: - return fmt.Errorf("PutEntitlementRecordsIfNewer: get: %w", getErr) - } - val, err := marshalRecord(r) - if err != nil { - return err - } - if err := batch.StageEntitlementPut(key, val); err != nil { - return err - } - written++ - } - if written == 0 { - return nil - } - if err := batch.Commit(writeOpts(e.opts.durability)); err != nil { - return err - } - e.noteEntitlementKeyspaceWrite() - return nil - }) -} - -// PutResourceTypeRecordsIfNewer writes resource_types only when newer. -func (e *Engine) PutResourceTypeRecordsIfNewer(ctx context.Context, records ...*v3.ResourceTypeRecord) error { - if len(records) == 0 { - return nil - } - return e.withWrite(func() error { - if err := e.requireCurrentSync(); err != nil { - return err - } - batch := e.db.NewRecordBatch() - defer batch.Close() - written := 0 - for _, r := range records { - if r == nil { - continue - } - key := encodeResourceTypeKey(r.GetExternalId()) - oldVal, closer, getErr := e.db.Get(key) - switch { - case getErr == nil: - write, err := discoveredAtIsNewerThanRaw(r.GetDiscoveredAt(), oldVal, resourceTypeDiscoveredAtField) - if err != nil { - closer.Close() - return fmt.Errorf("PutResourceTypeRecordsIfNewer: scan old discovered_at: %w", err) - } - closer.Close() - if !write { - continue - } - case errors.Is(getErr, pebble.ErrNotFound): - default: - return fmt.Errorf("PutResourceTypeRecordsIfNewer: get: %w", getErr) - } - val, err := marshalRecord(r) - if err != nil { - return err - } - if err := batch.StageResourceTypePut(key, val); err != nil { - return err - } - written++ - } - if written == 0 { - return nil - } - return batch.Commit(writeOpts(e.opts.durability)) - }) -} - -// discoveredAtIsNewer returns true iff incoming is strictly after -// existing. Matches SQLite's `EXCLUDED.discovered_at > X.discovered_at` -// semantics, including the NULL-propagation rules: -// -// - nil incoming → false (SQLite `NULL > X` is NULL, i.e. don't -// write). Adapter-level PutXxxIfNewer methods stamp DiscoveredAt -// to time.Now() before calling here, so production code never -// hits this branch; direct engine callers must supply a non-nil -// DiscoveredAt to mean "write this". -// - nil existing → true (no prior record at this key, so the -// incoming row wins by default — SQLite's INSERT-on-conflict -// reduces to a plain INSERT). -// - both non-nil → strict After comparison. -// -// Keep this in sync with extractAndStripExpansion / putGrantsInternal -// in pkg/dotc1z/grants.go if the SQLite IfNewer path ever changes. -func discoveredAtIsNewer(incoming, existing *timestamppb.Timestamp) bool { - if incoming == nil { - return false - } - if existing == nil { - return true - } - return incoming.AsTime().After(existing.AsTime()) -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/families.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/families.go index 72a104c7..9b205d06 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/families.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/families.go @@ -66,7 +66,7 @@ func (b *batch) Close() error { return b.b.Close() } // The primary record keyspaces plus their inline-maintained index // families and the digest-invalidation markers a record mutation owes. // Clients: the Put*Records paths, the expanded/synthesized grant -// writers, the IfNewer partial-sync paths, and delete paths. +// writers, and delete paths. // // RecordBatch exposes NO generic staging. The only way to stage a // record mutation is a typed Stage* operation (records.go) that diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/records.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/records.go index 7da733df..fb9cd3e0 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/records.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb/records.go @@ -16,7 +16,7 @@ package rawdb // - INLINE (StageGrantPutInline / StageGrantDelete): by_principal // and by_needs_expansion maintained inline; overwrite/delete // cleans BOTH; digest invalidation when digests are present. The -// PutGrantRecords and IfNewer paths, and post-seal deletes. +// PutGrantRecords paths and post-seal deletes. // - DEFERRED (StageGrantPutDeferred): the durable deferred-index // marker is armed FIRST (ArmDeferredGrantIndex — CAS-cheap per // record, crash-contract-ordered before the batch can commit), diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/raw_records.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/raw_records.go index a3db1e82..dd92a6b7 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/raw_records.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/raw_records.go @@ -2,114 +2,12 @@ package pebble import ( "fmt" - "math" - "time" "google.golang.org/protobuf/encoding/protowire" - "google.golang.org/protobuf/types/known/timestamppb" "github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb" ) -const ( - resourceTypeDiscoveredAtField protowire.Number = 6 - resourceDiscoveredAtField protowire.Number = 8 - entitlementDiscoveredAtField protowire.Number = 8 - grantDiscoveredAtField protowire.Number = 5 -) - -func discoveredAtIsNewerThanRaw(incoming *timestamppb.Timestamp, existingValue []byte, field protowire.Number) (bool, error) { - if incoming == nil { - return false, nil - } - existing, ok, err := rawDiscoveredAtNanos(existingValue, field) - if err != nil { - return false, err - } - if !ok { - return true, nil - } - return incoming.AsTime().UnixNano() > existing, nil -} - -func rawDiscoveredAtNanos(value []byte, field protowire.Number) (int64, bool, error) { - for len(value) > 0 { - num, typ, n := protowire.ConsumeTag(value) - if n < 0 { - return 0, false, protowire.ParseError(n) - } - value = value[n:] - if num != field { - n = protowire.ConsumeFieldValue(num, typ, value) - if n < 0 { - return 0, false, protowire.ParseError(n) - } - value = value[n:] - continue - } - if typ != protowire.BytesType { - return 0, false, fmt.Errorf("raw record: discovered_at has wire type %v", typ) - } - ts, n := protowire.ConsumeBytes(value) - if n < 0 { - return 0, false, protowire.ParseError(n) - } - nanos, err := rawTimestampNanos(ts) - return nanos, true, err - } - return 0, false, nil -} - -func rawTimestampNanos(value []byte) (int64, error) { - var seconds int64 - var nanos int32 - for len(value) > 0 { - num, typ, n := protowire.ConsumeTag(value) - if n < 0 { - return 0, protowire.ParseError(n) - } - value = value[n:] - switch num { - case 1: - if typ != protowire.VarintType { - return 0, fmt.Errorf("raw record: timestamp seconds has wire type %v", typ) - } - v, n := protowire.ConsumeVarint(value) - if n < 0 { - return 0, protowire.ParseError(n) - } - if v > math.MaxInt64 { - return 0, fmt.Errorf("raw record: timestamp seconds exceeds int64: %d", v) - } - seconds = int64(v) - value = value[n:] - case 2: - if typ != protowire.VarintType { - return 0, fmt.Errorf("raw record: timestamp nanos has wire type %v", typ) - } - v, n := protowire.ConsumeVarint(value) - if n < 0 { - return 0, protowire.ParseError(n) - } - if v > math.MaxInt32 { - return 0, fmt.Errorf("raw record: timestamp nanos exceeds int32: %d", v) - } - nanos = int32(v) - value = value[n:] - default: - n = protowire.ConsumeFieldValue(num, typ, value) - if n < 0 { - return 0, protowire.ParseError(n) - } - value = value[n:] - } - } - if seconds > math.MaxInt64/int64(time.Second) { - return 0, fmt.Errorf("raw record: timestamp seconds overflow: %d", seconds) - } - return seconds*int64(time.Second) + int64(nanos), nil -} - // NOTE (2b): deleteResourceIndexesRaw / deleteGrantIndexesRaw are GONE. // Prior-row index cleanup is an obligation of rawdb's typed record ops // (StageGrantPutInline/StageGrantDelete derive cleanup keys from the diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/sync_runs.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/sync_runs.go index e87c58d6..2bf6ccad 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/sync_runs.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/sync_runs.go @@ -31,8 +31,8 @@ func (e *Engine) PutSyncRunRecord(ctx context.Context, r *v3.SyncRunRecord) erro } // AllowSealed: sync-run metadata is legitimately stamped on a finished // sync — ToPebble preserves the source ended_at, the sanitizer applies - // diff links / supports_diff, and the compactor renames the folded sync - // — all after EndSync sealed the engine. + // supports_diff, and the compactor renames the folded sync — all after + // EndSync sealed the engine. return e.withWriteAllowSealed(func() error { val, err := marshalRecord(r) if err != nil { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/translate_v2.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/translate_v2.go index b713de98..5d61af3c 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/translate_v2.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/translate_v2.go @@ -341,15 +341,32 @@ func V2ResourceToV3(syncID string, r *v2.Resource) *v3.ResourceRecord { }.Build() } return v3.ResourceRecord_builder{ - ResourceTypeId: r.GetId().GetResourceType(), - ResourceId: r.GetId().GetResource(), - DisplayName: r.GetDisplayName(), - Description: r.GetDescription(), - Parent: parent, - Annotations: r.GetAnnotations(), - CreatedAt: r.GetCreatedAt(), - Profile: r.GetProfile(), - Status: v2StatusToV3(r.GetStatus()), + ResourceTypeId: r.GetId().GetResourceType(), + ResourceId: r.GetId().GetResource(), + DisplayName: r.GetDisplayName(), + Description: r.GetDescription(), + Parent: parent, + Annotations: r.GetAnnotations(), + CreatedAt: r.GetCreatedAt(), + Profile: r.GetProfile(), + Status: v2StatusToV3(r.GetStatus()), + IconAssetExternalId: v2AssetExternalID(r.GetIcon()), + }.Build() +} + +func v2AssetExternalID(a *v2.AssetRef) string { + if a == nil { + return "" + } + return a.GetId() +} + +func assetExternalIDToV2AssetRef(assetExternalID string) *v2.AssetRef { + if assetExternalID == "" { + return nil + } + return v2.AssetRef_builder{ + Id: assetExternalID, }.Build() } @@ -401,6 +418,7 @@ func V3ResourceToV2(r *v3.ResourceRecord) *v2.Resource { Profile: r.GetProfile(), Status: v3StatusToV2(r.GetStatus()), CreatedAt: r.GetCreatedAt(), + Icon: assetExternalIDToV2AssetRef(r.GetIconAssetExternalId()), }.Build() } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine_registry.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine_registry.go index db95f26b..180176b9 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine_registry.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine_registry.go @@ -203,8 +203,11 @@ func storeOptionsFromC1ZOptions(options *c1zOptions) StoreOptions { MaxDecodedPayloadBytes: maxDecodedPayloadBytes, MaxDecoderMemoryBytes: maxDecoderMemoryBytes, } + // Defensive only: NewStore, the sole caller, overwrites Engine with + // the selected driver's engine on the next line. Real default + // selection lives in selectStoreDriver. if out.Engine == "" { - out.Engine = c1zstore.EngineSQLite + out.Engine = c1zstore.EnginePebble } out.Pragmas = make([]StorePragma, 0, len(options.pragmas)) for _, p := range options.pragmas { @@ -218,7 +221,7 @@ func storeOptionsFromC1ZOptions(options *c1zOptions) StoreOptions { // Dispatch policy (in order): // // 1. If the file doesn't exist or is empty, honor the caller's -// `WithEngine(...)` choice (defaulting to EngineSQLite when +// `WithEngine(...)` choice (defaulting to EnginePebble when // unset). The about-to-be-written file gets the requested format. // 2. If the file exists with content, dispatch by the on-disk magic // byte — v1 → SQLite, v3 → whatever engine name the manifest @@ -226,6 +229,11 @@ func storeOptionsFromC1ZOptions(options *c1zOptions) StoreOptions { // case because we can't re-encode an existing file at open time; // the on-disk format is authoritative. This preserves the // read-any-format semantics that pre-dates the engine option. +// Exception: an EXPLICIT WithEngine(EnginePebble) on a writable v1 +// file converts it to Pebble in place. The EnginePebble default +// never triggers that conversion — an engine-less open of an +// existing v1 file stays SQLite, so read-intent callers (diff, +// stats, provisioning) don't rewrite files as a side effect. // // When the caller's WithEngine disagrees with the on-disk format we // log a warning so the divergence is observable. Callers that want @@ -235,7 +243,7 @@ func selectStoreDriver(ctx context.Context, outputFilePath string, options *c1zO l := ctxzap.Extract(ctx) requested := options.engine if requested == "" { - requested = c1zstore.EngineSQLite + requested = c1zstore.EnginePebble } stat, err := os.Stat(outputFilePath) // #nosec G703 -- c1z path is caller-controlled by API design. @@ -267,7 +275,9 @@ func selectStoreDriver(ctx context.Context, outputFilePath string, options *c1zO switch format { case C1ZFormatV1: // Maybe error if the file is read-only? - if requested == c1zstore.EnginePebble && !options.readOnly { + // Only an explicit pebble request converts; the engine default + // (options.engine == "") must not rewrite existing v1 files. + if options.engine == c1zstore.EnginePebble && !options.readOnly { // Close our header-read handle before converting: the conversion // renames a temp file over outputFilePath, which fails on Windows // if any handle to the destination is still open. Nil out f so @@ -277,11 +287,11 @@ func selectStoreDriver(ctx context.Context, outputFilePath string, options *c1zO if closeErr != nil { return nil, closeErr } - l.Debug("converting existing v1 c1z to pebble", zap.String("output_file_path", outputFilePath)) + l.Info("converting existing v1 c1z to pebble", zap.String("output_file_path", outputFilePath)) if err := convertExistingV1C1ZFile(ctx, outputFilePath, pebbleOpenOptionsFromC1Z(options)); err != nil { return nil, fmt.Errorf("select-store-driver: convert existing v1 c1z to pebble: %w", err) } - l.Debug("converted existing v1 c1z to pebble", zap.String("output_file_path", outputFilePath)) + l.Info("converted existing v1 c1z to pebble", zap.String("output_file_path", outputFilePath)) return requireEngineDriver(c1zstore.EnginePebble) } fileEngine = c1zstore.EngineSQLite diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/pebble_store.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/pebble_store.go index c1dcc309..1683ab87 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/pebble_store.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/pebble_store.go @@ -35,10 +35,7 @@ var _ connectorstore.Writer = (*pebbleStore)(nil) // the source/destination store (pkg/c1zsanitize keeps those interfaces // unexported). The assertions below make a refactor that drops one of these // methods from either engine break the build here, rather than silently -// disarming the sanitizer's sync-graph-metadata preservation. -type sanitizeSyncLinkWriter interface { - SetSyncLink(ctx context.Context, syncID string, linkedSyncID string) error -} +// disarming the sanitizer's sync-run-metadata preservation. type sanitizeSupportsDiffWriter interface { SetSupportsDiff(ctx context.Context, syncID string) error } @@ -47,10 +44,8 @@ type sanitizeSyncRunMetadataReader interface { } var ( - _ sanitizeSyncLinkWriter = (*pebbleStore)(nil) _ sanitizeSupportsDiffWriter = (*pebbleStore)(nil) _ sanitizeSyncRunMetadataReader = (*pebbleStore)(nil) - _ sanitizeSyncLinkWriter = (*C1File)(nil) _ sanitizeSupportsDiffWriter = (*C1File)(nil) _ sanitizeSyncRunMetadataReader = (*C1File)(nil) ) @@ -238,42 +233,12 @@ type pebbleStore struct { // route SQLite *C1File handles today. var _ c1zstore.Store = (*pebbleStore)(nil) -// FileOps overrides the Adapter-level FileOps for two reasons: -// -// - CloneSync threads the pebbleStore's configured payload encoding -// into the destination c1z (otherwise clone output would always -// use the default TAR_ZSTD); and -// - GenerateSyncDiff writes a NEW sync into THIS store, so it must -// flip the dirty bit — without it, Close would skip the envelope -// save and the diff sync would exist only in the discarded temp -// directory. +// FileOps overrides the Adapter-level FileOps so CloneSync threads the +// pebbleStore's configured payload encoding into the destination c1z +// (otherwise clone output would always use the default TAR_ZSTD). +// Clone/isolate write a separate file, so no dirty-marking is needed. func (s *pebbleStore) FileOps() c1zstore.FileOps { - return pebbleStoreFileOps{inner: s.FileOpsWithEncoding(s.payloadEncoding), store: s} -} - -// pebbleStoreFileOps wraps the Adapter-level FileOps to route the one -// mutating-in-place method (GenerateSyncDiff) through the store's -// dirty-marking path. CloneSync writes a separate file and passes -// through unchanged. -type pebbleStoreFileOps struct { - inner c1zstore.FileOps - store *pebbleStore -} - -func (f pebbleStoreFileOps) CloneSync(ctx context.Context, outPath string, syncID string, opts ...c1zstore.CloneSyncOption) error { - return f.inner.CloneSync(ctx, outPath, syncID, opts...) -} - -func (f pebbleStoreFileOps) CopyIsolateSync(ctx context.Context, outPath string, syncID string, opts ...c1zstore.CloneSyncOption) error { - return f.inner.CopyIsolateSync(ctx, outPath, syncID, opts...) -} - -func (f pebbleStoreFileOps) GenerateSyncDiff(ctx context.Context, baseSyncID, appliedSyncID string) (string, error) { - diffSyncID, err := f.inner.GenerateSyncDiff(ctx, baseSyncID, appliedSyncID) - if err != nil { - return "", err - } - return diffSyncID, f.store.markDirty(nil) + return s.FileOpsWithEncoding(s.payloadEncoding) } // SyncMeta overrides the Adapter-level SyncMeta so the MUTATING @@ -364,6 +329,18 @@ func (s *pebbleStore) PebbleEngine() *pebble.Engine { return s.Engine } +func (s *pebbleStore) GrantGenerationDigest(ctx context.Context) (c1zstore.GrantGenerationDigest, bool, error) { + root, ok, err := s.GetGrantDigestGlobalRoot(ctx) + if err != nil || !ok { + return c1zstore.GrantGenerationDigest{}, ok, err + } + return c1zstore.GrantGenerationDigest{ + Hash: append([]byte(nil), root.Hash...), + Count: root.Count, + ABIVersion: pebble.GrantDigestABIVersion, + }, true, nil +} + // CloseEngineOnly closes the Pebble engine without removing the // store's unpacked temp directory, refusing to discard a dirty // writable store. Consumed by the compactor's chunk lifecycle via @@ -498,36 +475,29 @@ func (s *pebbleStore) PutAsset(ctx context.Context, assetRef *v2.AssetRef, conte return s.markDirty(s.Engine.PutAsset(ctx, assetRef, contentType, data)) } -// SetSupportsDiff marks the given sync as diff-capable, matching the -// SQLite engine's sync_runs.supports_diff column. The c1z sanitizer -// carries this marker from a source sync to its sanitized copy so the -// output remains usable wherever the source was. Delegates to the -// SyncMeta sub-store's MarkSyncSupportsDiff. -func (s *pebbleStore) SetSupportsDiff(ctx context.Context, syncID string) error { - return s.markDirty(s.SyncMeta().MarkSyncSupportsDiff(ctx, syncID)) +// PutEntitlementGraphBlob / GetEntitlementGraphBlob / DeleteEntitlementGraphBlob +// expose the entitlement-graph sidecar (see pkg/sync's EntitlementGraphStore). +// The blob format is owned by pkg/sync/expand; the store treats it as opaque. +func (s *pebbleStore) PutEntitlementGraphBlob(ctx context.Context, data []byte) error { + return s.markDirty(s.PutEntitlementGraphSidecar(ctx, data)) } -// SetSyncLink records linkedSyncID as the diff partner of syncID on the -// sync-run record (v3 linked_sync_id), matching the SQLite engine. -// -// This is implemented for connectorstore.Writer parity but is NOT -// reached by the c1z sanitizer's Pebble path: a v3 Pebble c1z holds -// exactly one sync, so there is never a second sync to link to. Cross- -// file linkage is unpreservable on either engine regardless, because -// sanitize mints fresh destination sync ids. -func (s *pebbleStore) SetSyncLink(ctx context.Context, syncID string, linkedSyncID string) error { - if syncID == "" { - return fmt.Errorf("SetSyncLink: empty syncID") - } - r, err := s.GetSyncRunRecord(ctx, syncID) - if err != nil { - return fmt.Errorf("SetSyncLink: get: %w", err) - } - r.SetLinkedSyncId(linkedSyncID) - if err := s.PutSyncRunRecord(ctx, r); err != nil { - return fmt.Errorf("SetSyncLink: put: %w", err) - } - return s.markDirty(nil) +func (s *pebbleStore) GetEntitlementGraphBlob(ctx context.Context) ([]byte, error) { + return s.GetEntitlementGraphSidecar(ctx) +} + +func (s *pebbleStore) DeleteEntitlementGraphBlob(ctx context.Context) error { + return s.markDirty(s.DeleteEntitlementGraphSidecar(ctx)) +} + +// SetSupportsDiff marks the given sync's grant expansion as complete, +// matching the SQLite engine's sync_runs.supports_diff column (the name +// is historical; the marker now gates `baton rollback-expansion`). The +// c1z sanitizer carries this marker from a source sync to its sanitized +// copy so the output remains usable wherever the source was. Delegates +// to the SyncMeta sub-store's MarkSyncSupportsDiff. +func (s *pebbleStore) SetSupportsDiff(ctx context.Context, syncID string) error { + return s.markDirty(s.SyncMeta().MarkSyncSupportsDiff(ctx, syncID)) } func (s *pebbleStore) PutGrants(ctx context.Context, grants ...*v2.Grant) error { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go index 18a7fe5e..fe34588a 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sql_helpers.go @@ -352,7 +352,8 @@ func listConnectorObjects[T proto.Message](ctx context.Context, c *C1File, table return ret, nextPageToken, nil } -// This is required for sync diffs to work. Its not much slower. +// Deterministic marshaling keeps stored blobs byte-comparable across +// writes (compaction change detection, digests). Its not much slower. var protoMarshaler = proto.MarshalOptions{Deterministic: true} // prepareSingleConnectorObjectRow processes a single message and returns the prepared record. @@ -424,7 +425,8 @@ func prepareConnectorObjectRowsParallel[T proto.Message]( protoMarshallers := make([]proto.MarshalOptions, numWorkers) for i := range numWorkers { - // Deterministic marshaling is required for sync diffs to work. Its not much slower. + // Deterministic marshaling keeps stored blobs byte-comparable + // across writes. Its not much slower. protoMarshallers[i] = proto.MarshalOptions{Deterministic: true} } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sync_runs.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sync_runs.go index 61af474b..8bc0bf27 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sync_runs.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/sync_runs.go @@ -107,7 +107,12 @@ func (r *syncRunsTable) Migrations(ctx context.Context, db *goqu.Database) (bool migrated = true } - // Check if linked_sync_id column exists + // linked_sync_id is vestigial: only the removed diff-sync feature ever + // wrote non-empty values, and nothing reads it anymore. The column stays + // in the schema (and this migration stays) so every opened file has a + // uniform sync_runs shape — CloneSync/SnapshotTo copy rows using the + // source's PRAGMA table_info column list into a fresh-DDL destination, + // and older SDKs still SELECT the column by name. var linkedSyncIDExists int err = db.QueryRowContext(ctx, fmt.Sprintf("select count(*) from pragma_table_info('%s') where name='linked_sync_id'", r.Name())).Scan(&linkedSyncIDExists) if err != nil { @@ -266,7 +271,7 @@ func (c *C1File) getLatestUnfinishedSync(ctx context.Context, syncType connector q := c.db.From(syncRuns.Name()) q = q.Select( "sync_id", "started_at", "ended_at", "sync_token", "sync_type", - "parent_sync_id", "linked_sync_id", "supports_diff", + "parent_sync_id", "supports_diff", "ingest_invariant_generation", "ingest_invariant_coverage", "ingest_invariant_mode", "stats", ) @@ -288,7 +293,7 @@ func (c *C1File) getLatestUnfinishedSync(ctx context.Context, syncType connector var generation, coverageJSON, mode string err = row.Scan( &ret.ID, &ret.StartedAt, &ret.EndedAt, &ret.SyncToken, &ret.Type, - &ret.ParentSyncID, &ret.LinkedSyncID, &ret.SupportsDiff, + &ret.ParentSyncID, &ret.SupportsDiff, &generation, &coverageJSON, &mode, &statsBytes, ) if err != nil { @@ -323,7 +328,7 @@ func (c *C1File) getFinishedSync(ctx context.Context, offset uint, syncType conn q := c.db.From(syncRuns.Name()) q = q.Select( "sync_id", "started_at", "ended_at", "sync_token", "sync_type", - "parent_sync_id", "linked_sync_id", "supports_diff", + "parent_sync_id", "supports_diff", "ingest_invariant_generation", "ingest_invariant_coverage", "ingest_invariant_mode", "stats", ) @@ -352,7 +357,7 @@ func (c *C1File) getFinishedSync(ctx context.Context, offset uint, syncType conn var generation, coverageJSON, mode string err = row.Scan( &ret.ID, &ret.StartedAt, &ret.EndedAt, &ret.SyncToken, &ret.Type, - &ret.ParentSyncID, &ret.LinkedSyncID, &ret.SupportsDiff, + &ret.ParentSyncID, &ret.SupportsDiff, &generation, &coverageJSON, &mode, &statsBytes, ) if err != nil { @@ -398,7 +403,7 @@ func (c *C1File) ListSyncRuns(ctx context.Context, pageToken string, pageSize ui q := c.db.From(syncRuns.Name()).Prepared(true) q = q.Select( "id", "sync_id", "started_at", "ended_at", "sync_token", "sync_type", - "parent_sync_id", "linked_sync_id", "supports_diff", + "parent_sync_id", "supports_diff", "ingest_invariant_generation", "ingest_invariant_coverage", "ingest_invariant_mode", "stats", ) @@ -440,7 +445,7 @@ func (c *C1File) ListSyncRuns(ctx context.Context, pageToken string, pageSize ui var generation, coverageJSON, mode string err := rows.Scan( &rowId, &data.ID, &data.StartedAt, &data.EndedAt, &data.SyncToken, &data.Type, - &data.ParentSyncID, &data.LinkedSyncID, &data.SupportsDiff, + &data.ParentSyncID, &data.SupportsDiff, &generation, &coverageJSON, &mode, &statsBytes, ) if err != nil { @@ -542,7 +547,7 @@ func (c *C1File) getSync(ctx context.Context, syncID string) (*c1zstore.SyncRun, q := c.db.From(syncRuns.Name()) q = q.Select( "sync_id", "started_at", "ended_at", "sync_token", "sync_type", - "parent_sync_id", "linked_sync_id", "supports_diff", + "parent_sync_id", "supports_diff", "ingest_invariant_generation", "ingest_invariant_coverage", "ingest_invariant_mode", "stats", ) @@ -557,7 +562,7 @@ func (c *C1File) getSync(ctx context.Context, syncID string) (*c1zstore.SyncRun, var generation, coverageJSON, mode string err = row.Scan( &ret.ID, &ret.StartedAt, &ret.EndedAt, &ret.SyncToken, &ret.Type, - &ret.ParentSyncID, &ret.LinkedSyncID, &ret.SupportsDiff, + &ret.ParentSyncID, &ret.SupportsDiff, &generation, &coverageJSON, &mode, &statsBytes, ) if err != nil { @@ -757,10 +762,6 @@ func (c *C1File) StartNewSync(ctx context.Context, syncType connectorstore.SyncT return "", status.Errorf(codes.InvalidArgument, "parent sync id must be empty for resources only sync") } case connectorstore.SyncTypePartial: - case connectorstore.SyncTypePartialUpserts, connectorstore.SyncTypePartialDeletions: - // Diff syncs carry the base sync as their parent; the linked - // pairing (upserts ↔ deletions) is set separately via - // SetSyncLink since the partner's id may not exist yet. case connectorstore.SyncTypeAny: return "", status.Errorf(codes.InvalidArgument, "sync cannot be started with SyncTypeAny") default: @@ -780,10 +781,6 @@ func (c *C1File) StartNewSync(ctx context.Context, syncType connectorstore.SyncT } func (c *C1File) insertSyncRun(ctx context.Context, syncID string, syncType connectorstore.SyncType, parentSyncID string) error { - return c.insertSyncRunWithLink(ctx, syncID, syncType, parentSyncID, "") -} - -func (c *C1File) insertSyncRunWithLink(ctx context.Context, syncID string, syncType connectorstore.SyncType, parentSyncID string, linkedSyncID string) error { if c.readOnly { return ErrReadOnly } @@ -800,7 +797,6 @@ func (c *C1File) insertSyncRunWithLink(ctx context.Context, syncID string, syncT "sync_token": "", "sync_type": syncType, "parent_sync_id": parentSyncID, - "linked_sync_id": linkedSyncID, "grants_backfilled": 1, // New syncs do not require grants backfill. }) @@ -883,8 +879,10 @@ func (c *C1File) endSyncRun(ctx context.Context, syncID string) error { return nil } -// SetSupportsDiff marks the given sync as supporting diff operations. -// This indicates the sync has SQL-layer grant metadata (is_expandable) properly populated. +// SetSupportsDiff marks the given sync's data collection as complete with +// SQL-layer grant metadata (is_expandable) properly populated. The name is +// historical (the marker once gated diff-sync generation); today it gates +// `baton rollback-expansion`, which refuses syncs without the marker. func (c *C1File) SetSupportsDiff(ctx context.Context, syncID string) error { ctx, span := tracer.Start(ctx, "C1File.SetSupportsDiff") var err error @@ -1000,43 +998,6 @@ func (c *C1File) clearIngestInvariantVerification(ctx context.Context, syncID st return nil } -// SetSyncLink sets the linked_sync_id of an existing sync run. Diff -// sync pairs (partial_upserts ↔ partial_deletions) reference each -// other bidirectionally; a writer rebuilding such a pair cannot supply -// the link at StartNewSync time because the partner's id is minted by -// the store, so the pairing is applied after both runs exist. -func (c *C1File) SetSyncLink(ctx context.Context, syncID string, linkedSyncID string) error { - ctx, span := tracer.Start(ctx, "C1File.SetSyncLink") - var err error - defer func() { uotel.EndSpanWithError(span, err) }() - - if c.readOnly { - return ErrReadOnly - } - if syncID == "" { - return status.Errorf(codes.InvalidArgument, "sync id is required") - } - - q := c.db.Update(syncRuns.Name()) - q = q.Set(goqu.Record{ - "linked_sync_id": linkedSyncID, - }) - q = q.Where(goqu.C("sync_id").Eq(syncID)) - - query, args, err := q.ToSQL() - if err != nil { - return err - } - - _, err = c.db.ExecContext(ctx, query, args...) - if err != nil { - return err - } - c.dbUpdated.Store(true) - - return nil -} - // When context deadline is exceeded, go-sqlite can return a SQLITE_INTERRUPT error. // If that happens, wrapSqliteInterruptError wraps this error and returns context.DeadlineExceeded. // This allows sync cleanup to return ErrSyncNotComplete and resume its work on the next run. diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/to_pebble.go b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/to_pebble.go index 91d5c1fe..ede01029 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/to_pebble.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/to_pebble.go @@ -200,9 +200,8 @@ type syncIDPreservingStarter interface { // // When the source has no sync runs at all, "" writes an empty Pebble c1z (so // convert-open succeeds on never-synced files). If sync runs exist but none -// match the selected resolve behavior (e.g. diff-only under Newest), "" -// returns an error rather than discarding data. Pass an explicit syncID to -// convert a specific sync (including diff syncs for fixture seeding). The +// match the selected resolve behavior, "" returns an error rather than +// discarding data. Pass an explicit syncID to convert a specific sync. The // destination sync is written ended when the source was finished; when the // source was unfinished, EndSync still runs (indexes/digests/stats/flush) but // ended_at is cleared and the source sync_token is preserved so the sync @@ -214,11 +213,10 @@ type syncIDPreservingStarter interface { // since nothing resumes a sealed sync and the connector lifecycle deletes them // at that point anyway. // -// The sync's lineage columns — parent_sync_id, linked_sync_id, supports_diff — -// are preserved too. They reference syncs that the single-sync destination -// cannot hold, but those references are meaningful across files: dropping them -// would make a converted partial read as a standalone snapshot and a -// diff-capable sync read as non-diffable. +// The sync's lineage columns — parent_sync_id, supports_diff — are preserved +// too. The parent reference names a sync the single-sync destination cannot +// hold, but it is meaningful across files: dropping it would make a converted +// partial read as a standalone snapshot. // // The Pebble engine is registered statically with dotc1z; no extra // imports are needed before calling. @@ -399,7 +397,6 @@ func (c *C1File) ToPebble(ctx context.Context, outPath string, syncID string, op if err != nil { return nil, fmt.Errorf("to-pebble: load destination sync metadata: %w", err) } - rec.SetLinkedSyncId(sync.LinkedSyncID) rec.SetSupportsDiff(sync.SupportsDiff) // Localized on the way in: these scanned wall clocks become absolute // instants in the Pebble record, and Pebble's resume cutoff compares @@ -459,9 +456,7 @@ func (c *C1File) ToPebble(ctx context.Context, outPath string, syncID string, op } // discardedSyncs lists the source syncs a conversion that keeps keepSyncID -// leaves behind, in sync_runs order. Diff-pair syncs are included: they are -// dropped from the artifact too, and their absence is what an operator chasing -// a missing delta needs to see. +// leaves behind, in sync_runs order. // // Metadata only. ListSyncRuns reads the sync_runs rows and parses the cached // stats blob when one is present; unlike GetSync it never recomputes stats, so @@ -532,14 +527,6 @@ func discardedSyncFields(discarded []DiscardedSync) []zap.Field { // chosen when it is all there is (newest started_at among them), since // convert-open must not fail on such a file. Unfinished syncs within the // cutoff are live work and keep competing on started_at alone. -// -// The excluded types are the diff pair written by attached-file diffing, -// partial_upserts and partial_deletions: each holds one side of a delta and -// is meaningless converted alone. GenerateSyncDiff's delta sync is NOT -// excluded — it is stored as a plain partial (diff.go), indistinguishable -// from a targeted sync by type, parent_sync_id, or supports_diff — so on a -// file that was just diffed and holds no newer sync, "" resolves to the -// delta. func (c *C1File) resolveConvertSyncID(ctx context.Context) (string, error) { q := c.db.From(syncRuns.Name()).Prepared(true). Select("sync_id"). diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/exit/exit.go b/vendor/github.com/conductorone/baton-sdk/pkg/exit/exit.go new file mode 100644 index 00000000..225de5a9 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/exit/exit.go @@ -0,0 +1,53 @@ +package exit + +import ( + "context" + "errors" + "fmt" + "os" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Exit exits the program with a code based on the error. +// Exit codes correspond to GRPC status codes. +// If err is nil, exit code is 0. +// Common errors such as context cancelled and deadline exceeded exit with the corresponding GRPC status code. +// Other errors exit with code 2, which is GRPC status code Unknown. +func Exit(err error) { + os.Exit(exitCode(err)) +} + +// LogExit logs the error to stderr & calls Exit(), which exits the program with a code based on the error. +func LogExit(err error) { + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + Exit(err) +} + +func exitCode(err error) int { + if err == nil { + return 0 + } + + if grpcErr, ok := status.FromError(err); ok { + if grpcErr.Code() == codes.OK { + // An error with code OK should never happen. + return int(codes.Internal) + } + return int(grpcErr.Code()) + } + + if errors.Is(err, context.Canceled) { + return int(codes.Canceled) + } + + if errors.Is(err, context.DeadlineExceeded) { + return int(codes.DeadlineExceeded) + } + + // Otherwise, exit with code 2, which is GRPC status code Unknown. + return int(codes.Unknown) +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go index f859d8fc..5da905ef 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/default_relationships.go @@ -6,7 +6,6 @@ var DefaultRelationships = []SchemaFieldRelationship{ FieldsRequiredTogether(createTicketField, ticketTemplatePathField), FieldsRequiredTogether(bulkCreateTicketField, bulkTicketTemplatePathField), FieldsRequiredTogether(getTicketField, ticketIDField), - FieldsRequiredTogether(diffSyncsField, diffSyncsBaseSyncField, diffSyncsAppliedSyncField), FieldsRequiredTogether(compactSyncsField, compactSyncIDsField, compactFilePathsField, compactOutputDirectoryField), FieldsMutuallyExclusive( grantEntitlementField, @@ -27,7 +26,6 @@ var DefaultRelationships = []SchemaFieldRelationship{ deleteResourceTypeField, rotateCredentialsTypeField, eventFeedField, - diffSyncsField, compactSyncsField, ListTicketSchemasField, ), diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go b/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go index 16857042..29e60583 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go @@ -136,25 +136,6 @@ var ( WithDescription("The resource type IDs to sync"), WithPersistent(true), WithExportTarget(ExportTargetNone)) - diffSyncsField = BoolField( - "diff-syncs", - WithDescription("Create a new partial SyncID from a base and applied sync."), - WithHidden(true), - WithPersistent(true), - WithExportTarget(ExportTargetNone), - ) - diffSyncsBaseSyncField = StringField("base-sync-id", - WithDescription("The base sync to diff from."), - WithHidden(true), - WithPersistent(true), - WithExportTarget(ExportTargetNone), - ) - diffSyncsAppliedSyncField = StringField("applied-sync-id", - WithDescription("The sync to show diffs when applied to the base sync."), - WithHidden(true), - WithPersistent(true), - WithExportTarget(ExportTargetNone), - ) compactSyncsField = BoolField("compact-syncs", WithDescription("Provide a list of sync files to compact into a single c1z file and sync ID."), @@ -355,10 +336,10 @@ var ( })) // StorageEngineField selects the dotc1z storage engine for sync tasks. - // Empty uses the baton-sdk default (sqlite for new files). + // Empty uses the baton-sdk default (pebble for new files). StorageEngineField = StringField("storage-engine", WithDescription("The storage engine to use when opening the sync c1z file: sqlite or pebble. "+ - "Leave unset to use the baton-sdk default."), + "Defaults to pebble when unset."), WithPersistent(true), WithExportTarget(ExportTargetNone), WithString(func(r *StringRuler) { @@ -439,9 +420,6 @@ var DefaultFields = append([]SchemaField{ externalResourceEntitlementIdFilter, externalResourceTraitsField, KeepPreviousSyncC1ZField, - diffSyncsField, - diffSyncsBaseSyncField, - diffSyncsAppliedSyncField, compactSyncIDsField, compactFilePathsField, compactOutputDirectoryField, diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sdk/version.go b/vendor/github.com/conductorone/baton-sdk/pkg/sdk/version.go index 658ecef7..191e00bc 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sdk/version.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sdk/version.go @@ -1,3 +1,3 @@ package sdk -const Version = "v0.24.5" +const Version = "v0.25.0" diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go index 23a9f9c9..cf741dcd 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph.go @@ -2,7 +2,9 @@ package expand import ( "context" + "fmt" "iter" + "slices" "sort" "strings" @@ -132,6 +134,111 @@ func (g *EntitlementGraph) IsExpanded() bool { return true } +// MarkExpansionComplete records that every edge has been evaluated and the +// graph passed cycle detection. Persisted graphs must carry these facts so the +// next expansion can safely treat them as completed bases. +func (g *EntitlementGraph) MarkExpansionComplete() { + for edgeID, edge := range g.Edges { + edge.IsExpanded = true + g.Edges[edgeID] = edge + } + g.HasNoCycles = true + g.Actions = nil +} + +// ValidateCompleted checks the durable facts required before a graph may be +// reused as an incremental-expansion base. +func (g *EntitlementGraph) ValidateCompleted() error { + if g == nil { + return fmt.Errorf("graph is nil") + } + if !g.Loaded { + return fmt.Errorf("graph is not fully loaded") + } + if !g.HasNoCycles { + return fmt.Errorf("graph has not completed cycle detection") + } + if !g.IsExpanded() { + return fmt.Errorf("graph has unexpanded edges") + } + maxNodeID := 0 + for nodeID, node := range g.Nodes { + if nodeID > maxNodeID { + maxNodeID = nodeID + } + if node.Id != nodeID { + return fmt.Errorf("node map key %d does not match node id %d", nodeID, node.Id) + } + for _, entitlementID := range node.EntitlementIDs { + if got, ok := g.EntitlementsToNodes[entitlementID]; !ok || got != nodeID { + return fmt.Errorf("entitlement %q does not map back to node %d", entitlementID, nodeID) + } + } + } + if g.NextNodeID < maxNodeID { + return fmt.Errorf("next node id %d is below existing maximum %d", g.NextNodeID, maxNodeID) + } + for entitlementID, nodeID := range g.EntitlementsToNodes { + node, ok := g.Nodes[nodeID] + if !ok || !slices.Contains(node.EntitlementIDs, entitlementID) { + return fmt.Errorf("entitlement map entry %q points to inconsistent node %d", entitlementID, nodeID) + } + } + maxEdgeID := 0 + for edgeID, edge := range g.Edges { + if edgeID > maxEdgeID { + maxEdgeID = edgeID + } + if edge.EdgeID != edgeID { + return fmt.Errorf("edge map key %d does not match edge id %d", edgeID, edge.EdgeID) + } + if _, ok := g.Nodes[edge.SourceID]; !ok { + return fmt.Errorf("edge %d has missing source node %d", edgeID, edge.SourceID) + } + if _, ok := g.Nodes[edge.DestinationID]; !ok { + return fmt.Errorf("edge %d has missing destination node %d", edgeID, edge.DestinationID) + } + if got := g.SourcesToDestinations[edge.SourceID][edge.DestinationID]; got != edgeID { + return fmt.Errorf("edge %d missing from source adjacency", edgeID) + } + if got := g.DestinationsToSources[edge.DestinationID][edge.SourceID]; got != edgeID { + return fmt.Errorf("edge %d missing from destination adjacency", edgeID) + } + } + if g.NextEdgeID < maxEdgeID { + return fmt.Errorf("next edge id %d is below existing maximum %d", g.NextEdgeID, maxEdgeID) + } + for sourceID, destinations := range g.SourcesToDestinations { + for destinationID, edgeID := range destinations { + edge, ok := g.Edges[edgeID] + if !ok || edge.SourceID != sourceID || edge.DestinationID != destinationID { + return fmt.Errorf("source adjacency %d->%d points to inconsistent edge %d", sourceID, destinationID, edgeID) + } + } + } + for destinationID, sources := range g.DestinationsToSources { + for sourceID, edgeID := range sources { + edge, ok := g.Edges[edgeID] + if !ok || edge.SourceID != sourceID || edge.DestinationID != destinationID { + return fmt.Errorf("destination adjacency %d<-%d points to inconsistent edge %d", destinationID, sourceID, edgeID) + } + } + } + return nil +} + +// HasCollapsedCycles reports whether full expansion collapsed an SCC into a +// multi-entitlement node. Such a graph no longer records its internal edges, +// so it cannot safely detect an edge removal that splits the SCC. +func (g *EntitlementGraph) HasCollapsedCycles() bool { + for _, node := range g.Nodes { + if len(node.EntitlementIDs) > 1 { + return true + } + } + return false +} + // IsEntitlementExpanded returns true if all the outgoing edges for the given entitlement have been expanded. func (g *EntitlementGraph) IsEntitlementExpanded(entitlementID string) bool { node := g.GetNode(entitlementID) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph_blob.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph_blob.go new file mode 100644 index 00000000..7e394fc6 --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/graph_blob.go @@ -0,0 +1,91 @@ +package expand + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore" +) + +// graphBlobEnvelope is the serialized form of the entitlement-graph sidecar +// stored in a c1z (instead of bloating the sync token). SyncID guards +// against reading a graph inherited from a different sync (e.g. a fold-copied +// compaction base). +type graphBlobEnvelope struct { + FormatVersion uint32 `json:"format_version"` + SyncID string `json:"sync_id"` + GrantDigest *c1zstore.GrantGenerationDigest `json:"grant_digest,omitempty"` + Graph *EntitlementGraph `json:"graph"` +} + +const graphBlobFormatVersion uint32 = 2 + +// MarshalGraphBlob serializes a legacy, unbound graph blob for compatibility +// tests. Transient state is stripped first (a reload rebuilds it). +// +// The blob has no grant-generation digest, so sync.GraphFromStore deliberately +// rejects it for incremental reuse. Production persistence must use +// MarshalGraphBlobWithGrantDigest. +func MarshalGraphBlob(syncID string, g *EntitlementGraph) ([]byte, error) { + return marshalGraphBlob(syncID, g, nil) +} + +// MarshalGraphBlobWithGrantDigest binds the graph to the exact sealed grant +// generation. Graph reuse requires this binding. +func MarshalGraphBlobWithGrantDigest(syncID string, g *EntitlementGraph, digest c1zstore.GrantGenerationDigest) ([]byte, error) { + if len(digest.Hash) == 0 || digest.ABIVersion == 0 { + return nil, fmt.Errorf("marshal graph blob: incomplete grant digest") + } + digest.Hash = append([]byte(nil), digest.Hash...) + return marshalGraphBlob(syncID, g, &digest) +} + +func marshalGraphBlob(syncID string, g *EntitlementGraph, digest *c1zstore.GrantGenerationDigest) ([]byte, error) { + if g == nil { + return nil, fmt.Errorf("marshal graph blob: nil graph") + } + graphCopy := *g + graphCopy.ClearTransientState() + data, err := json.Marshal(graphBlobEnvelope{FormatVersion: graphBlobFormatVersion, SyncID: syncID, GrantDigest: digest, Graph: &graphCopy}) + if err != nil { + return nil, fmt.Errorf("marshal graph blob: %w", err) + } + return data, nil +} + +// UnmarshalGraphBlob parses a graph for compatibility tests while discarding +// its grant-generation binding. Returns (nil, nil) when the blob belongs to a +// different sync than wantSyncID (stale inherited sidecar); pass "" to skip +// the guard. +// +// The returned graph must not drive incremental reuse. Production readers +// must use UnmarshalGraphBlobWithGrantDigest and verify the returned digest. +func UnmarshalGraphBlob(data []byte, wantSyncID string) (*EntitlementGraph, error) { + graph, _, err := UnmarshalGraphBlobWithGrantDigest(data, wantSyncID) + return graph, err +} + +// UnmarshalGraphBlobWithGrantDigest returns the persisted grant-generation +// binding along with the graph. A nil digest means the blob is unbound and +// must not be reused incrementally. +func UnmarshalGraphBlobWithGrantDigest(data []byte, wantSyncID string) (*EntitlementGraph, *c1zstore.GrantGenerationDigest, error) { + var env graphBlobEnvelope + if err := json.Unmarshal(data, &env); err != nil { + return nil, nil, fmt.Errorf("unmarshal graph blob: %w", err) + } + if env.FormatVersion != graphBlobFormatVersion { + return nil, nil, nil + } + if wantSyncID != "" && env.SyncID != wantSyncID { + return nil, nil, nil + } + if env.Graph == nil { + return nil, nil, nil + } + env.Graph.reinitMaps() + if env.GrantDigest != nil { + env.GrantDigest.Hash = bytes.Clone(env.GrantDigest.Hash) + } + return env.Graph, env.GrantDigest, nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/incremental.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/incremental.go new file mode 100644 index 00000000..f5db117c --- /dev/null +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/incremental.go @@ -0,0 +1,590 @@ +package expand + +import ( + "context" + "errors" + "fmt" + "sort" + + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + reader_v2 "github.com/conductorone/baton-sdk/pb/c1/reader/v2" + batonGrant "github.com/conductorone/baton-sdk/pkg/types/grant" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ClearTransientState drops the graph's expansion working state — the action +// queue, the projection plan, and metrics — which a persisted graph doesn't +// need for a later reload and which can bloat the sync token. The structural +// graph (nodes, edges, mappings) is untouched. +func (g *EntitlementGraph) ClearTransientState() { + g.Actions = nil + g.ExpansionPlan = nil + g.ExpansionMetrics = nil +} + +// Clone returns a structural deep copy of the graph. Incremental expansion +// mutates the graph, so callers must not share maps or slices with the base. +// This deliberately avoids a JSON round trip: graph cloning is paid on every +// eligible incremental attempt and benchmark evidence showed serialization +// dominated both CPU and allocation at whale scale. +func (g *EntitlementGraph) Clone() (*EntitlementGraph, error) { + if g == nil { + return nil, fmt.Errorf("clone entitlement graph: nil graph") + } + out := &EntitlementGraph{ + NextNodeID: g.NextNodeID, + NextEdgeID: g.NextEdgeID, + Nodes: make(map[int]Node, len(g.Nodes)), + EntitlementsToNodes: make(map[string]int, len(g.EntitlementsToNodes)), + SourcesToDestinations: cloneNestedIntMap(g.SourcesToDestinations), + DestinationsToSources: cloneNestedIntMap(g.DestinationsToSources), + Edges: make(map[int]Edge, len(g.Edges)), + Loaded: g.Loaded, + Depth: g.Depth, + HasNoCycles: g.HasNoCycles, + } + for id, node := range g.Nodes { + node.EntitlementIDs = append([]string(nil), node.EntitlementIDs...) + out.Nodes[id] = node + } + for entitlementID, nodeID := range g.EntitlementsToNodes { + out.EntitlementsToNodes[entitlementID] = nodeID + } + for id, edge := range g.Edges { + edge.ResourceTypeIDs = append([]string(nil), edge.ResourceTypeIDs...) + out.Edges[id] = edge + } + out.Actions = make([]*EntitlementGraphAction, len(g.Actions)) + for i, action := range g.Actions { + if action == nil { + continue + } + actionCopy := *action + actionCopy.Descendants = append([]ActionDescendant(nil), action.Descendants...) + actionCopy.ResourceTypeIDs = append([]string(nil), action.ResourceTypeIDs...) + out.Actions[i] = &actionCopy + } + if g.ExpansionPlan != nil { + plan := *g.ExpansionPlan + plan.Order = append([]int(nil), g.ExpansionPlan.Order...) + plan.ProjectionSources = append([]string(nil), g.ExpansionPlan.ProjectionSources...) + out.ExpansionPlan = &plan + } + if g.ExpansionMetrics != nil { + metrics := *g.ExpansionMetrics + out.ExpansionMetrics = &metrics + } + return out, nil +} + +func cloneNestedIntMap(source map[int]map[int]int) map[int]map[int]int { + out := make(map[int]map[int]int, len(source)) + for outer, inner := range source { + innerCopy := make(map[int]int, len(inner)) + for key, value := range inner { + innerCopy[key] = value + } + out[outer] = innerCopy + } + return out +} + +// reinitMaps replaces nil maps (json leaves absent maps nil) so a +// deserialized graph is immediately usable. +func (g *EntitlementGraph) reinitMaps() { + if g.Nodes == nil { + g.Nodes = map[int]Node{} + } + if g.EntitlementsToNodes == nil { + g.EntitlementsToNodes = map[string]int{} + } + if g.SourcesToDestinations == nil { + g.SourcesToDestinations = map[int]map[int]int{} + } + if g.DestinationsToSources == nil { + g.DestinationsToSources = map[int]map[int]int{} + } + if g.Edges == nil { + g.Edges = map[int]Edge{} + } +} + +// ErrIncrementalFallback means a new edge closed a cycle; the caller should +// re-run a full expansion, which handles cycles correctly. +var ErrIncrementalFallback = errors.New("incremental expansion: change introduces a cycle, fall back to full expansion") + +// ErrIncrementalRevocationDecline means the change is revocation-shaped (an +// existing edge's spec narrowed — shallow-ified, filter tightened, or a source +// dropped), which incremental expansion cannot apply without removing grants. +// The caller declines to full expansion. This is the named hook a future +// tombstone/deletion stage flips from "decline" to "apply deletions". +var ErrIncrementalRevocationDecline = errors.New("incremental expansion: revocation-shaped change, fall back to full expansion") + +// ErrIncrementalDenseChangeDecline means the affected closure is large enough +// that normal full expansion is the safer bounded-cost path. +var ErrIncrementalDenseChangeDecline = errors.New("incremental expansion: dense affected graph, fall back to full expansion") + +const ( + incrementalDenseGraphMinNodes = 1000 + incrementalMaxAffectedPercent = 10 +) + +// NewEdge is one edge to fold in: members of Source also get Destination. +type NewEdge struct { + SourceEntitlementID string + DestEntitlementID string + Shallow bool + ResourceTypeIDs []string +} + +// IncrementalResult reports the impacted subgraph and how many grants were written. +type IncrementalResult struct { + EntitlementsWalked []string + GrantsWritten int +} + +// IncrementalExpander folds new edges into an already-expanded graph and +// propagates the change to only the affected subgraph, reading and writing +// through the same ExpanderStore as the full expander. +// +// Preconditions: graph is a prior completed expansion's graph (edges already +// expanded), and store holds that expansion's grants. Additions only; a new +// edge that closes a cycle returns ErrIncrementalFallback. +type IncrementalExpander struct { + store ExpanderStore + graph *EntitlementGraph + entitlementCache map[string]*v2.Entitlement +} + +func NewIncrementalExpander(store ExpanderStore, graph *EntitlementGraph) *IncrementalExpander { + return &IncrementalExpander{ + store: store, + graph: graph, + entitlementCache: make(map[string]*v2.Entitlement), + } +} + +// ExpandChanges recomputes grants for only the subgraph affected by a set of +// changes. Both kinds seed the walk: newEdges (new expandable relationships, +// added to the graph here) via their destinations, and changedEntitlementIDs +// (entitlements whose membership changed) via their own node. The second kind +// is essential — a membership change adds no edge, so seeding only from +// newEdges would silently drop it. +// +// changedEntitlementIDs is direction-neutral: it names entitlements whose +// membership changed in EITHER direction (added or removed). Whether a removal +// is actually applied is a WRITE-behavior concern, not a seed concern — today +// this method only adds grants (never removes), so callers decline +// revocation-shaped changes to full expansion. When a future stage learns to +// apply deletions, removed-membership entitlements flow through this same +// parameter with no signature change. +// +// The walk reads current membership from the store, so changed members +// (already merged in) propagate without being passed in. Returns +// ErrIncrementalFallback if a new edge closes a cycle. +func (ie *IncrementalExpander) ExpandChanges(ctx context.Context, newEdges []NewEdge, changedEntitlementIDs []string) (*IncrementalResult, error) { + if len(newEdges) == 0 && len(changedEntitlementIDs) == 0 { + return &IncrementalResult{}, nil + } + + seeds := make(map[int]struct{}) + for _, e := range newEdges { + ie.graph.AddEntitlementID(e.SourceEntitlementID) + ie.graph.AddEntitlementID(e.DestEntitlementID) + if err := ie.graph.AddEdge(ctx, e.SourceEntitlementID, e.DestEntitlementID, e.Shallow, e.ResourceTypeIDs); err != nil { + return nil, fmt.Errorf("incremental expansion: add edge %s->%s: %w", e.SourceEntitlementID, e.DestEntitlementID, err) + } + if dst := ie.graph.GetNode(e.DestEntitlementID); dst != nil { + seeds[dst.Id] = struct{}{} + } + } + + // A changed entitlement seeds its own node so descendants are recomputed. + // Entitlements not in the graph have nothing downstream — safely ignored. + for _, entitlementID := range changedEntitlementIDs { + if n := ie.graph.GetNode(entitlementID); n != nil { + seeds[n.Id] = struct{}{} + } + } + + if len(seeds) == 0 { + return &IncrementalResult{}, nil + } + + if cyclic, _ := ie.graph.ComputeCyclicComponents(ctx); len(cyclic) > 0 { + return nil, ErrIncrementalFallback + } + + // Only nodes forward-reachable from a seed are touched. + affected := ie.forwardReachable(seeds) + if len(ie.graph.Nodes) >= incrementalDenseGraphMinNodes && + len(affected)*100 > len(ie.graph.Nodes)*incrementalMaxAffectedPercent { + return nil, ErrIncrementalDenseChangeDecline + } + + // Topological order only the affected closure. Parents outside this set + // were finalized by the base expansion and are read from the store; sorting + // the untouched graph made K=1 work scale with total graph size. + order, err := topologicalAffectedNodeOrder(ie.graph, affected) + if err != nil { + return nil, fmt.Errorf("incremental expansion: topological order: %w", err) + } + + result := &IncrementalResult{} + for _, nodeID := range order { + if err := ctx.Err(); err != nil { + return nil, err // cancelled / run-duration exceeded + } + if _, ok := affected[nodeID]; !ok { + continue + } + node, ok := ie.graph.Nodes[nodeID] + if !ok { + continue + } + for _, destEntitlementID := range node.EntitlementIDs { + written, err := ie.recomputeDestination(ctx, nodeID, destEntitlementID) + if err != nil { + return nil, err + } + result.EntitlementsWalked = append(result.EntitlementsWalked, destEntitlementID) + result.GrantsWritten += written + } + } + ie.graph.MarkExpansionComplete() + return result, nil +} + +func topologicalAffectedNodeOrder(g *EntitlementGraph, affected map[int]struct{}) ([]int, error) { + inDegree := make(map[int]int, len(affected)) + for nodeID := range affected { + if _, ok := g.Nodes[nodeID]; ok { + inDegree[nodeID] = 0 + } + } + for sourceID := range inDegree { + for destinationID := range g.SourcesToDestinations[sourceID] { + if _, ok := inDegree[destinationID]; ok { + inDegree[destinationID]++ + } + } + } + frontier := make(intMinHeap, 0, len(inDegree)) + for nodeID, degree := range inDegree { + if degree == 0 { + frontier.push(nodeID) + } + } + order := make([]int, 0, len(inDegree)) + for len(frontier) > 0 { + nodeID := frontier.pop() + order = append(order, nodeID) + for childID := range g.SourcesToDestinations[nodeID] { + if _, ok := inDegree[childID]; !ok { + continue + } + inDegree[childID]-- + if inDegree[childID] == 0 { + frontier.push(childID) + } + } + } + if len(order) != len(inDegree) { + return nil, fmt.Errorf("incremental expansion: affected graph contains a cycle or dangling edge") + } + return order, nil +} + +// intMinHeap keeps the smallest ready node at the front without re-sorting +// every ready node after each insertion. +type intMinHeap []int + +func (h *intMinHeap) push(nodeID int) { + *h = append(*h, nodeID) + for child := len(*h) - 1; child > 0; { + parent := (child - 1) / 2 + if (*h)[parent] <= (*h)[child] { + break + } + (*h)[parent], (*h)[child] = (*h)[child], (*h)[parent] + child = parent + } +} + +func (h *intMinHeap) pop() int { + root := (*h)[0] + last := len(*h) - 1 + (*h)[0] = (*h)[last] + *h = (*h)[:last] + + for parent := 0; ; { + left := 2*parent + 1 + if left >= len(*h) { + break + } + child := left + right := left + 1 + if right < len(*h) && (*h)[right] < (*h)[left] { + child = right + } + if (*h)[parent] <= (*h)[child] { + break + } + (*h)[parent], (*h)[child] = (*h)[child], (*h)[parent] + parent = child + } + + return root +} + +func (ie *IncrementalExpander) forwardReachable(seeds map[int]struct{}) map[int]struct{} { + reached := make(map[int]struct{}) + queue := make([]int, 0, len(seeds)) + for id := range seeds { + reached[id] = struct{}{} + queue = append(queue, id) + } + for len(queue) > 0 { + cur := queue[0] + queue = queue[1:] + for child := range ie.graph.SourcesToDestinations[cur] { + if _, ok := reached[child]; !ok { + reached[child] = struct{}{} + queue = append(queue, child) + } + } + } + return reached +} + +// incrementalFlushChunk caps buffered new grants before a flush, so a whale +// destination doesn't materialize its whole output. Mirrors the full +// expander's expansionDirtyFlushChunk. A var only so tests can lower it. +var incrementalFlushChunk = 10000 + +// recomputeDestination writes destEntitlementID's implied grants that aren't +// already present, returning how many. Source grants stream a page at a time +// and writes flush in chunks, so peak memory is one page + one flush buffer + +// the destination's existing-key set — not the whole source or output. +func (ie *IncrementalExpander) recomputeDestination(ctx context.Context, nodeID int, destEntitlementID string) (int, error) { + destEnt, err := ie.getEntitlement(ctx, destEntitlementID) + if err != nil { + return 0, err + } + if destEnt == nil { + // Dangling ref: skip-with-warn, matching the full evaluator (don't + // error into a fallback). + ctxzap.Extract(ctx).Warn("incremental expansion: destination entitlement not in store; skipping", + zap.String("entitlement_id", destEntitlementID)) + return 0, nil + } + + // 1. Accumulate, per principal, the union of sources contributed by all + // incoming edges. Streaming reads keep only one source page live, but the + // contribution map holds one entry per distinct principal across ALL + // sources feeding this destination — the same worst-case fan-in footprint + // the full expander's per-destination reduce carries. + contrib := make(map[string]*principalContribution) + for sourceNodeID, edgeID := range ie.graph.DestinationsToSources[nodeID] { + edge, ok := ie.graph.Edges[edgeID] + if !ok { + continue + } + sourceNode, ok := ie.graph.Nodes[sourceNodeID] + if !ok { + continue + } + for _, sourceEntitlementID := range sourceNode.EntitlementIDs { + // The store's read path requires a full entitlement record (with + // resource refs), not a bare id — fetch it. A dangling ref (source + // not in the store) is skipped-with-warn, matching the full evaluator. + sourceEnt, err := ie.getEntitlement(ctx, sourceEntitlementID) + if err != nil { + return 0, err + } + if sourceEnt == nil { + ctxzap.Extract(ctx).Warn("incremental expansion: source entitlement not in store; skipping", + zap.String("entitlement_id", sourceEntitlementID)) + continue + } + perGrantErr := ie.forEachGrant(ctx, sourceEnt, edge.ResourceTypeIDs, func(sourceGrant *v2.Grant) error { + // Shared definition of "contributes" with the full expander: + // rejects nil-principal grants, off-type principals, and + // non-direct grants over shallow edges. + if !grantContributesOverEdge(sourceGrant, sourceEntitlementID, edge) { + return nil + } + // Directness is relative to the source entitlement (matches the + // full expander): a plain direct grant or one whose sources map + // records this entitlement counts as direct. + isSourceDirect := isGrantDirectOnEntitlement(sourceGrant, sourceEntitlementID) + principal := sourceGrant.GetPrincipal() + pid := principal.GetId() + key := pid.GetResourceType() + "\x00" + pid.GetResource() + pc := contrib[key] + if pc == nil { + pc = &principalContribution{principal: principal} + contrib[key] = pc + } + pc.addSource(sourceEntitlementID, isSourceDirect) + return nil + }) + if perGrantErr != nil { + return 0, perGrantErr + } + } + } + if len(contrib) == 0 { + return 0, nil + } + + buf := make([]*v2.Grant, 0, incrementalFlushChunk) + written := 0 + flush := func() error { + if len(buf) == 0 { + return nil + } + if err := ie.store.StoreExpandedGrants(ctx, buf...); err != nil { + return fmt.Errorf("incremental expansion: store grants on %s: %w", destEntitlementID, err) + } + written += len(buf) + buf = buf[:0] + return nil + } + + // 2. Merge contributions into the destination's existing grants (union the + // sources map, upgrade direct-ness), streaming one page at a time. Only a + // grant that actually changed is rewritten. A principal can hold several + // grant rows on one entitlement (connector-authored IDs are arbitrary), and + // the full expander merges the contribution into every row sharing the + // principal key — so record matches in a side set instead of consuming the + // contribution on the first row, and drop them from contrib only after the + // whole destination has streamed. + matched := make(map[string]struct{}) + mergeErr := ie.forEachGrant(ctx, destEnt, nil, func(g *v2.Grant) error { + pid := g.GetPrincipal().GetId() + key := pid.GetResourceType() + "\x00" + pid.GetResource() + pc := contrib[key] + if pc == nil { + return nil + } + matched[key] = struct{}{} + updated := mergeContributionIntoExistingGrant(g, destEntitlementID, pc.sources) + if updated == nil { + return nil // already had these sources — no write + } + buf = append(buf, updated) + if len(buf) >= incrementalFlushChunk { + return flush() + } + return nil + }) + if mergeErr != nil { + return 0, mergeErr + } + for key := range matched { + delete(contrib, key) + } + + // 3. Whatever is left in contrib are brand-new principals. Sort for + // deterministic (byte-stable) output. + newKeys := make([]string, 0, len(contrib)) + for key := range contrib { + newKeys = append(newKeys, key) + } + sort.Strings(newKeys) + for _, key := range newKeys { + pc := contrib[key] + grant, err := newExpandedGrantWithSources(destEnt, pc.principal, pc.sources) + if err != nil { + return 0, fmt.Errorf("incremental expansion: build grant on %s: %w", destEntitlementID, err) + } + buf = append(buf, grant) + if len(buf) >= incrementalFlushChunk { + if err := flush(); err != nil { + return 0, err + } + } + } + + if err := flush(); err != nil { + return 0, err + } + return written, nil +} + +// principalContribution accumulates the source entitlements contributing one +// principal to a destination. sources is a small slice (fan-in is tiny), deduped +// by entitlement id with direct-ness upgraded to true if any contribution is direct. +type principalContribution struct { + principal *v2.Resource + sources batonGrant.Sources +} + +func (pc *principalContribution) addSource(entitlementID string, isDirect bool) { + for i := range pc.sources { + if pc.sources[i].EntitlementID == entitlementID { + if isDirect && !pc.sources[i].IsDirect { + pc.sources[i].IsDirect = true + } + return + } + } + pc.sources = append(pc.sources, batonGrant.Source{EntitlementID: entitlementID, IsDirect: isDirect}) +} + +// getEntitlement fetches an entitlement, returning (nil, nil) for a dangling +// ref (NotFound) so callers skip it — matching the full evaluator, which treats +// NotFound as skip rather than a hard error. +func (ie *IncrementalExpander) getEntitlement(ctx context.Context, entitlementID string) (*v2.Entitlement, error) { + if entitlement, ok := ie.entitlementCache[entitlementID]; ok { + return entitlement, nil + } + resp, err := ie.store.GetEntitlement(ctx, reader_v2.EntitlementsReaderServiceGetEntitlementRequest_builder{ + EntitlementId: entitlementID, + }.Build()) + if err != nil { + if status.Code(err) == codes.NotFound { + ie.entitlementCache[entitlementID] = nil + return nil, nil + } + return nil, fmt.Errorf("incremental expansion: get entitlement %s: %w", entitlementID, err) + } + if resp == nil { + ie.entitlementCache[entitlementID] = nil + return nil, nil + } + entitlement := resp.GetEntitlement() + ie.entitlementCache[entitlementID] = entitlement + return entitlement, nil +} + +// forEachGrant streams an entitlement's grants (filtered by resourceTypeIDs) +// one page at a time, invoking fn per grant — never materializing the whole +// set. entitlement must be a full record (with resource refs); the store's read +// path rejects bare-id entitlements. +func (ie *IncrementalExpander) forEachGrant(ctx context.Context, entitlement *v2.Entitlement, resourceTypeIDs []string, fn func(*v2.Grant) error) error { + pageToken := "" + for { + resp, err := ie.store.ListGrantsForEntitlement(ctx, reader_v2.GrantsReaderServiceListGrantsForEntitlementRequest_builder{ + Entitlement: entitlement, + PrincipalResourceTypeIds: resourceTypeIDs, + PageToken: pageToken, + }.Build()) + if err != nil { + return fmt.Errorf("incremental expansion: list grants for %s: %w", entitlement.GetId(), err) + } + for _, g := range resp.GetList() { + if err := fn(g); err != nil { + return err + } + } + pageToken = resp.GetNextPageToken() + if pageToken == "" { + return nil + } + } +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/topological_merge.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/topological_merge.go index c3b35807..37eee020 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/topological_merge.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/expand/topological_merge.go @@ -429,11 +429,7 @@ func (e *Expander) driveTopologicalLayer( // queue. The topological evaluators expand the whole graph in one pass, so they // finalize all edges together at the end rather than per action. func (e *Expander) markExpansionComplete() { - for edgeID, edge := range e.graph.Edges { - edge.IsExpanded = true - e.graph.Edges[edgeID] = edge - } - e.graph.Actions = nil + e.graph.MarkExpansionComplete() } func sortedCopy(in []string) []string { diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/external_principal_index.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/external_principal_index.go index 06792e02..22f4de78 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/external_principal_index.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/external_principal_index.go @@ -190,7 +190,7 @@ func foldKey(s string) string { var b strings.Builder b.Grow(len(s)) for _, r := range s { - b.WriteRune(foldRune(r)) + _, _ = b.WriteRune(foldRune(r)) } return b.String() } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/ingest_invariants.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/ingest_invariants.go index 5a278504..3bb9efbe 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/ingest_invariants.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/ingest_invariants.go @@ -163,8 +163,7 @@ func invariantVerdict(err error) error { return &invariantVerdictError{err: err} // TypeScopedGrants) land, they register here against I7 and I8 // respectively — type-granularity scopes are exactly the shapes those // referential checks exist for. -// -//nolint:gosec // G101 false positive: "PageTokens" is an annotation name, not a credential. +// #nosec G101 -- "PageTokens" is an annotation name, not a credential. var sideEffectAnnotationCoverage = map[string]string{ "c1.connector.v2.GrantExpandable": "I1: response-loop expansion arming (SetNeedsExpansion) + needs_expansion column persistence; store-derived probe arrives with replay", "c1.connector.v2.ExternalResourceMatch": "I2: response-loop match arming (SetHasExternalResourcesGrants); store-derived existence-bit repair arrives with replay", @@ -449,10 +448,43 @@ func ingestInvariantHaltStages() []string { // store-level function so store-producing pipelines without a syncer // (the compactor's expand pass) can enforce the same contract. func RunIngestInvariants(ctx context.Context, store connectorstore.Reader, policy IngestInvariantsPolicy) error { - _, err := runIngestInvariants(ctx, store, policy) + _, err := RunIngestInvariantsWithVerification(ctx, store, policy) return err } +// RunIngestInvariantsWithVerification evaluates the invariant pass and returns +// the verification metadata a store-producing caller must persist after the +// sync is sealed. It does not write the marker itself: publishing proof before +// EndSync would allow an unfinished artifact to claim verification. +func RunIngestInvariantsWithVerification( + ctx context.Context, + store connectorstore.Reader, + policy IngestInvariantsPolicy, +) (*c1zstore.IngestInvariantVerification, error) { + coverage, err := runIngestInvariants(ctx, store, policy) + if err != nil { + return nil, err + } + return &c1zstore.IngestInvariantVerification{ + Generation: IngestInvariantGeneration, + Coverage: coverage, + Mode: ingestInvariantVerificationMode(policy), + }, nil +} + +func ingestInvariantVerificationMode(policy IngestInvariantsPolicy) c1zstore.IngestInvariantVerificationMode { + switch { + case policy.CompactionMerge && policy.FailFast: + return c1zstore.IngestInvariantVerificationModeCompactionMergeFailFast + case policy.CompactionMerge: + return c1zstore.IngestInvariantVerificationModeCompactionMerge + case policy.FailFast: + return c1zstore.IngestInvariantVerificationModeConnectorFailFast + default: + return c1zstore.IngestInvariantVerificationModeConnector + } +} + // runIngestInvariants returns the IDs of checks that actually completed. The // public wrapper intentionally retains its existing error-only API; the syncer // consumes the coverage to persist verification provenance. @@ -608,24 +640,11 @@ func (s *syncer) runIngestionInvariants(ctx context.Context) error { if s.testIngestHaltHook != nil { policy.halt = s.testIngestHaltHook } - coverage, err := runIngestInvariants(ctx, s.store, policy) + verification, err := RunIngestInvariantsWithVerification(ctx, s.store, policy) if err != nil { return err } - mode := c1zstore.IngestInvariantVerificationModeConnector - switch { - case policy.CompactionMerge && policy.FailFast: - mode = c1zstore.IngestInvariantVerificationModeCompactionMergeFailFast - case policy.CompactionMerge: - mode = c1zstore.IngestInvariantVerificationModeCompactionMerge - case policy.FailFast: - mode = c1zstore.IngestInvariantVerificationModeConnectorFailFast - } - s.pendingInvariantVerification = &c1zstore.IngestInvariantVerification{ - Generation: IngestInvariantGeneration, - Coverage: coverage, - Mode: mode, - } + s.pendingInvariantVerification = verification return nil } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/parallel_syncer.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/parallel_syncer.go index cc7111d4..35cbd737 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/parallel_syncer.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/parallel_syncer.go @@ -400,9 +400,11 @@ func (s *syncer) parallelSync( continue case SyncGrantExpansionOp: - // Mark the sync as supporting diff, but only if we're starting fresh. - // If we're resuming (graph has edges or a page token), we may be continuing - // from old code that didn't have this marker, so we must not set it. + // Stamp the supports_diff marker (data collection complete; the + // name is historical — it gates `baton rollback-expansion`), but + // only if we're starting fresh. If we're resuming (graph has edges + // or a page token), we may be continuing from old code that didn't + // have this marker, so we must not set it. entitlementGraph := s.state.EntitlementGraph(ctx) isResumingExpansion := entitlementGraph.Loaded || len(entitlementGraph.Edges) > 0 || stateAction.PageToken != "" if !isResumingExpansion { @@ -411,8 +413,8 @@ func (s *syncer) parallelSync( } if err := s.store.SyncMeta().MarkSyncSupportsDiff(ctx, s.syncID); err != nil { // No detached rescue on this exit (RFC 0009 §4.2): a - // metadata-only write for the unused diff-sync feature, - // with no progress since the loop-top checkpoint. + // metadata-only write, with no progress since the + // loop-top checkpoint. l.Error("failed to set supports_diff marker", zap.Error(err)) return warnings, err } diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go index 9fbd487e..ecec42c4 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/state.go @@ -1,6 +1,7 @@ package sync //nolint:revive,nolintlint // we can't change the package name for backwards compatibility import ( + "bytes" "context" "encoding/json" "errors" @@ -10,6 +11,7 @@ import ( "sync" "time" + "github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore" "github.com/conductorone/baton-sdk/pkg/sync/expand" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" @@ -34,7 +36,9 @@ type State interface { FinishAction(ctx context.Context, action *Action) NextPage(ctx context.Context, actionID string, pageToken string) error EntitlementGraph(ctx context.Context) *expand.EntitlementGraph + PeekEntitlementGraph() *expand.EntitlementGraph ClearEntitlementGraph(ctx context.Context) + ClearEntitlementGraphTransientState(ctx context.Context) Current() *Action GetAction(id string) *Action PeekMatchingActions(ctx context.Context, op ActionOp) []*Action @@ -86,6 +90,12 @@ func PrepareExpansionReplayToken(stateStr string) (string, error) { return "", err } st.SetNeedsExpansion() + // Clear any preserved entitlement graph. A graph preserved by + // WithPreserveEntitlementGraph has Loaded=true with every edge already + // marked expanded, so a replayed sync would skip graph loading and the + // expander would report done immediately — the replay would silently + // no-op. Clearing it makes the replay rebuild the graph from scratch. + st.ClearEntitlementGraph(context.Background()) if st.Current() == nil { // A finished sync deserializes with no action map, so seed one before // queuing the InitOp that drives the resumed run. @@ -97,6 +107,68 @@ func PrepareExpansionReplayToken(stateStr string) (string, error) { return st.Marshal() } +// GraphFromToken parses a legacy sync token and returns its entitlement graph +// for compatibility tests. It returns nil if the token carried no graph. +// +// Token graphs have no grant-generation binding and must not drive incremental +// reuse. Production readers must use GraphFromStore, which verifies that the +// sidecar graph describes the store's exact sealed grant generation. +func GraphFromToken(stateStr string) (*expand.EntitlementGraph, error) { + st := newState() + if err := st.Unmarshal(stateStr); err != nil { + return nil, err + } + return st.entitlementGraph, nil +} + +// EntitlementGraphStore is the optional store capability backing graph +// persistence in the c1z (Pebble implements it; SQLite does not). The blob +// format is owned by pkg/sync/expand. +type EntitlementGraphStore interface { + PutEntitlementGraphBlob(ctx context.Context, data []byte) error + GetEntitlementGraphBlob(ctx context.Context) ([]byte, error) + DeleteEntitlementGraphBlob(ctx context.Context) error +} + +// GraphFromStore loads the entitlement graph persisted in the c1z sidecar for +// syncID. Returns nil (no error) when the store lacks the capability, no graph +// was preserved, or the stored graph belongs to a different sync. +func GraphFromStore(ctx context.Context, store c1zstore.Store, syncID string) (*expand.EntitlementGraph, error) { + gs, ok := store.(EntitlementGraphStore) + if !ok { + return nil, nil + } + data, err := gs.GetEntitlementGraphBlob(ctx) + if err != nil { + return nil, err + } + if data == nil { + return nil, nil + } + graph, boundDigest, err := expand.UnmarshalGraphBlobWithGrantDigest(data, syncID) + if err != nil || graph == nil { + return graph, err + } + if boundDigest == nil { + return nil, nil + } + digestReader, ok := store.(c1zstore.GrantGenerationDigestReader) + if !ok { + return nil, nil + } + currentDigest, found, err := digestReader.GrantGenerationDigest(ctx) + if err != nil { + return nil, err + } + if !found || + boundDigest.Count != currentDigest.Count || + boundDigest.ABIVersion != currentDigest.ABIVersion || + !bytes.Equal(boundDigest.Hash, currentDigest.Hash) { + return nil, nil + } + return graph, nil +} + // ActionOp represents a sync operation. type ActionOp uint8 @@ -1093,11 +1165,28 @@ func (st *state) EntitlementGraph(ctx context.Context) *expand.EntitlementGraph return st.entitlementGraph } +// PeekEntitlementGraph returns the graph without allocating one when absent +// (unlike EntitlementGraph). Used by the preserve path to decide whether +// there is a graph worth persisting. +func (st *state) PeekEntitlementGraph() *expand.EntitlementGraph { + return st.entitlementGraph +} + // ClearEntitlementGraph clears the entitlement graph. This is meant to make the final sync token less confusing. func (st *state) ClearEntitlementGraph(ctx context.Context) { st.entitlementGraph = nil } +// ClearEntitlementGraphTransientState strips a preserved graph's expansion +// working state before the final checkpoint. A no-op when no graph was built — +// deliberately NOT EntitlementGraph(ctx), which would allocate an empty graph +// into the final token where prior behavior serialized none. +func (st *state) ClearEntitlementGraphTransientState(_ context.Context) { + if st.entitlementGraph != nil { + st.entitlementGraph.ClearTransientState() + } +} + func (st *state) GetCompletedActionsCount() uint64 { st.mtx.RLock() defer st.mtx.RUnlock() diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go b/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go index 516d3669..f69237ed 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/sync/syncer.go @@ -206,19 +206,20 @@ type syncer struct { // event (seed/dequeue/commit/abort/done) for post-hoc verification // of the queue contract. Nil in production: one pointer check per // queue operation. - testQueueAudit *queueAudit - connector types.ConnectorClient - state State - runDuration time.Duration - transitionHandler func(s Action) - progressHandler func(p *Progress) - tmpDir string - storageEngine c1zstore.Engine - skipFullSync bool - lastCheckPointTime time.Time - counts *progresslog.ProgressLog - targetedSyncResources []*v2.Resource - onlyExpandGrants bool + testQueueAudit *queueAudit + connector types.ConnectorClient + state State + runDuration time.Duration + transitionHandler func(s Action) + progressHandler func(p *Progress) + tmpDir string + storageEngine c1zstore.Engine + skipFullSync bool + lastCheckPointTime time.Time + counts *progresslog.ProgressLog + targetedSyncResources []*v2.Resource + onlyExpandGrants bool + preserveEntitlementGraph bool // compactionMergedStore marks the store as a pre-sealed artifact // this process did not collect (WithCompactionMergedStore — the // compactor's keep-newer merge and rollback-expansion's replay): @@ -270,6 +271,44 @@ type expanderStoreAdapter struct { store c1zstore.Store } +// NewExpanderStore adapts a c1zstore.Store into an expand.ExpanderStore, +// bridging engine differences (Pebble exposes StoreExpandedGrants on its +// Grants() sub-store, SQLite at top level). Use this instead of type-asserting +// the store, which is unsafe for Pebble. +func NewExpanderStore(store c1zstore.Store) expand.ExpanderStore { + return expanderStoreAdapter{store: store} +} + +// persistEntitlementGraphToStore binds the preserved graph to the exact sealed +// grant generation and writes both into the c1z sidecar. +func (s *syncer) persistEntitlementGraphToStore(ctx context.Context, syncID string, g *expand.EntitlementGraph) { + if g == nil { + return + } + gs, ok := s.store.(EntitlementGraphStore) + if !ok { + return + } + digestReader, ok := s.store.(c1zstore.GrantGenerationDigestReader) + if !ok { + return + } + digest, found, err := digestReader.GrantGenerationDigest(ctx) + if err != nil || !found { + ctxzap.Extract(ctx).Warn("preserve entitlement graph: sealed grant digest unavailable; graph will not be reusable", zap.Error(err)) + return + } + data, err := expand.MarshalGraphBlobWithGrantDigest(syncID, g, digest) + if err != nil { + ctxzap.Extract(ctx).Warn("preserve entitlement graph: marshal failed", zap.Error(err)) + return + } + if err := gs.PutEntitlementGraphBlob(ctx, data); err != nil { + ctxzap.Extract(ctx).Warn("preserve entitlement graph: sidecar write failed", zap.Error(err)) + return + } +} + func (a expanderStoreAdapter) GetEntitlement(ctx context.Context, req *reader_v2.EntitlementsReaderServiceGetEntitlementRequest) (*reader_v2.EntitlementsReaderServiceGetEntitlementResponse, error) { return a.store.GetEntitlement(ctx, req) } @@ -1058,8 +1097,22 @@ func (s *syncer) Sync(ctx context.Context) error { } // Force a checkpoint to clear completed actions & entitlement graph in sync_token. - s.state.ClearEntitlementGraph(ctx) - + // preserveEntitlementGraph keeps the graph for a later incremental + // expansion: written to the c1z sidecar when the store supports it (token + // stays skinny — a whale graph is megabytes), else kept in the final token. + // Transient working state is stripped either way; a reload rebuilds it. + var graphToPersist *expand.EntitlementGraph + if s.preserveEntitlementGraph { + s.state.ClearEntitlementGraphTransientState(ctx) + _, hasGraphSidecar := s.store.(EntitlementGraphStore) + _, hasGrantDigest := s.store.(c1zstore.GrantGenerationDigestReader) + if hasGraphSidecar && hasGrantDigest { + graphToPersist = s.state.PeekEntitlementGraph() + s.state.ClearEntitlementGraph(ctx) + } + } else { + s.state.ClearEntitlementGraph(ctx) + } err = s.Checkpoint(ctx, true) if err != nil { // Deliberately no detached rescue (RFC 0009 §4.2): the plan is @@ -1083,6 +1136,10 @@ func (s *syncer) Sync(ctx context.Context) error { if err != nil { return s.returnSyncError(l, span, err) } + // EndSync built the authoritative whole-file grant digest. Persisting the + // graph now binds it to that exact sealed grant generation. A crash before + // this write leaves no reusable graph and therefore fails safe. + s.persistEntitlementGraphToStore(ctx, syncID, graphToPersist) // The sync is sealed: publish the verification the invariant pass // staged. Marking only after EndSync keeps the marker off unfinished @@ -4077,6 +4134,15 @@ func WithCompactionMergedStore() SyncOpt { } } +// WithPreserveEntitlementGraph preserves the entitlement graph for later +// incremental expansion. Pebble stores it in the c1z sidecar; stores without +// that capability retain it in the final sync token as a legacy fallback. +func WithPreserveEntitlementGraph() SyncOpt { + return func(s *syncer) { + s.preserveEntitlementGraph = true + } +} + // WithDontExpandGrants sets whether to skip expanding grants. // This is used for speeding up service mode connectors and reducing their c1z upload size. // C1 will process the uploaded c1z and expand grants itself. diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/attached/attached.go b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/attached/attached.go index 97c1e50b..96e29359 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/attached/attached.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/attached/attached.go @@ -42,7 +42,6 @@ func NewAttachedCompactor(base, applied c1zstore.Store) (*Compactor, error) { } func latestFinishedCompactableSync(ctx context.Context, f *dotc1z.C1File) (*reader_v2.SyncRun, error) { - // Compaction must NOT operate on diff syncs (partial_upserts / partial_deletions). // We want the latest finished "snapshot-like" sync. candidates := []connectorstore.SyncType{ connectorstore.SyncTypeFull, @@ -82,11 +81,7 @@ func (c *Compactor) Compact(ctx context.Context) error { return fmt.Errorf("failed to get base sync: %w", err) } if baseSync == nil { - return fmt.Errorf( - "no finished compactable sync found in base (diff sync types %q/%q are not compactable)", - string(connectorstore.SyncTypePartialUpserts), - string(connectorstore.SyncTypePartialDeletions), - ) + return fmt.Errorf("no finished compactable sync found in base") } appliedSync, err := latestFinishedCompactableSync(ctx, c.applied) @@ -94,11 +89,7 @@ func (c *Compactor) Compact(ctx context.Context) error { return fmt.Errorf("failed to get applied sync: %w", err) } if appliedSync == nil { - return fmt.Errorf( - "no finished compactable sync found in applied (diff sync types %q/%q are not compactable)", - string(connectorstore.SyncTypePartialUpserts), - string(connectorstore.SyncTypePartialDeletions), - ) + return fmt.Errorf("no finished compactable sync found in applied") } l := ctxzap.Extract(ctx) diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor.go b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor.go index b1cfeee3..8b561eb3 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor.go @@ -8,19 +8,24 @@ import ( "os" "path" "path/filepath" + "sort" "time" + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" reader_v2 "github.com/conductorone/baton-sdk/pb/c1/reader/v2" "github.com/conductorone/baton-sdk/pkg/connectorstore" "github.com/conductorone/baton-sdk/pkg/dotc1z" "github.com/conductorone/baton-sdk/pkg/dotc1z/c1zstore" "github.com/conductorone/baton-sdk/pkg/sdk" "github.com/conductorone/baton-sdk/pkg/sync" + "github.com/conductorone/baton-sdk/pkg/sync/expand" "github.com/conductorone/baton-sdk/pkg/synccompactor/attached" "github.com/conductorone/baton-sdk/pkg/tempdir" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.opentelemetry.io/otel" "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/conductorone/baton-sdk/pkg/uotel" ) @@ -44,10 +49,30 @@ type Compactor struct { syncLimit int c1zOptions []dotc1z.C1ZOption skipGrantExpansion bool + failFastInvariants bool + // incrementalExpansion enables diff-aware expansion. The compactor loads the + // graph itself from entries[0], so callers cannot pair a graph with the wrong + // artifact. incrementalBaseGraph holds that validated, store-loaded graph. + // The set of changed entitlements is derived from the applied increments + // during expansion, not supplied by the caller. + incrementalExpansion bool + incrementalBaseGraph *expand.EntitlementGraph + // incrementalExpansionRan records whether the diff-aware path actually + // handled expansion (vs falling back to full). Read by tests to prove the + // fast path ran rather than silently falling back. + incrementalExpansionRan bool + // incrementalTestHook is a package-private fault seam used by crash/retry + // tests. Production compactions leave it nil. + incrementalTestHook func(stage string) error + // foldChangedEntitlementIDs: changed-entitlement set collected by the + // Pebble fold; nil when no fold ran (derive fallback). + foldChangedEntitlementIDs map[string]struct{} // engine selects the storage engine for the compacted output. - // Empty means EngineSQLite (the default; behavior is unchanged and - // the output is byte-identical to the pre-engine-option compactor). - // EnginePebble produces a v3 Pebble c1z via a native record merge. + // Empty means "follow the inputs": Compact resolves it via + // inferEngineFromInputs (any Pebble input → Pebble, all-SQLite → + // SQLite), so the compactor does NOT follow the dotc1z engine + // default. EnginePebble produces a v3 Pebble c1z via a native + // record merge. engine c1zstore.Engine // pebbleMode optionally forces the Pebble merge strategy; the zero // value (Auto) lets the compactor choose. See WithPebbleCompactorMode. @@ -111,7 +136,7 @@ func (c *Compactor) inferEngineFromInputs() (c1zstore.Engine, error) { if entry == nil || entry.FilePath == "" { continue } - f, err := os.Open(entry.FilePath) // #nosec G304 - compaction inputs are caller-provided c1z paths. + f, err := os.Open(entry.FilePath) // #nosec G304,G703 -- compaction inputs are intentionally caller-provided c1z paths. if err != nil { return "", fmt.Errorf("infer compactor engine from %s: %w", entry.FilePath, err) } @@ -169,6 +194,22 @@ func WithTmpDir(tempDir string) Option { } } +// WithIncrementalExpansion enables diff-aware grant expansion during compaction. +// The compactor loads the graph from entries[0] via sync.GraphFromStore; +// missing, stale, incomplete, or inconsistent graphs safely fall back. The set of +// entitlements whose membership changed is derived from the applied increments +// during expansion (not supplied by the caller), so new members propagate. A +// new edge that closes a cycle falls back to full expansion; nil baseGraph +// (default) = full. +// +// Additions-only: a revocation-shaped change (a narrowed edge spec) auto-declines +// to full expansion; removals are not propagated incrementally. +func WithIncrementalExpansion() Option { + return func(c *Compactor) { + c.incrementalExpansion = true + } +} + // Deprecated: There is now only one compactor type, so this option is no longer needed. func WithCompactorType(compactorType CompactorType) Option { return func(c *Compactor) { @@ -205,6 +246,14 @@ func WithSkipGrantExpansion() Option { } } +// WithFailFastInvariants promotes every ingestion-invariant verdict to a hard +// failure on both incremental and full expansion paths. +func WithFailFastInvariants() Option { + return func(c *Compactor) { + c.failFastInvariants = true + } +} + func NewCompactor(ctx context.Context, outputDir string, compactableSyncs []*CompactableSync, opts ...Option) (*Compactor, func() error, error) { if len(compactableSyncs) < 2 { return nil, nil, ErrNotEnoughFilesToCompact @@ -348,13 +397,12 @@ func (c *Compactor) Compact(ctx context.Context) (*CompactableSync, error) { opts = append(opts, dotc1z.WithDecoderPool(c.decoderPool)) } - if c.resolvedEngine() == c1zstore.EnginePebble { - // Force the resolved engine last so a stray engine passed via - // WithC1ZOptions cannot mislabel the artifact. - c.compactedC1z, err = dotc1z.NewStore(ctx, destFilePath, append(opts, dotc1z.WithEngine(c1zstore.EnginePebble))...) - } else { - c.compactedC1z, err = dotc1z.NewStore(ctx, destFilePath, opts...) - } + // Force the resolved engine last: a stray engine passed via + // WithC1ZOptions cannot mislabel the artifact, and the dotc1z + // engine default (Pebble) cannot leak into a SQLite compaction — + // the destination is a new file, so an engine-less open would + // otherwise create a v3 store under the SQLite merge path. + c.compactedC1z, err = dotc1z.NewStore(ctx, destFilePath, append(opts, dotc1z.WithEngine(c.resolvedEngine()))...) if err != nil { l.Error("doOneCompaction failed: could not create c1z file", zap.Error(err)) return nil, err @@ -461,11 +509,21 @@ func (c *Compactor) Compact(ctx context.Context) (*CompactableSync, error) { c.compactedC1z = nil } + if c.incrementalExpansionRan { + if err := c.runIncrementalTestHook("before_publish"); err != nil { + return nil, err + } + } // Move last compacted file to the destination dir finalPath := path.Join(c.destDir, fmt.Sprintf("compacted-%s.c1z", newSyncId)) if err := cpFile(ctx, destFilePath, finalPath); err != nil { return nil, err } + if c.incrementalExpansionRan { + if err := c.runIncrementalTestHook("after_publish"); err != nil { + return nil, err + } + } if !filepath.IsAbs(finalPath) { abs, err := filepath.Abs(finalPath) @@ -478,7 +536,7 @@ func (c *Compactor) Compact(ctx context.Context) (*CompactableSync, error) { } func cpFile(ctx context.Context, sourcePath string, destPath string) error { - err := os.Rename(sourcePath, destPath) + err := os.Rename(sourcePath, destPath) // #nosec G703 -- compaction source and destination paths are intentional API inputs. if err == nil { return nil } @@ -492,7 +550,7 @@ func cpFile(ctx context.Context, sourcePath string, destPath string) error { } defer source.Close() - destination, err := os.Create(destPath) + destination, err := os.Create(destPath) // #nosec G703 -- the caller intentionally selects the compacted artifact destination. if err != nil { return fmt.Errorf("failed to create destination file: %w", err) } @@ -565,8 +623,667 @@ func (c *Compactor) doOneCompaction(ctx context.Context, cs *CompactableSync) er return nil } +// expandGrantsIncremental runs a diff-aware expansion over the compacted c1z. +// errIncrementalFatal marks incremental-expansion errors that must FAIL the +// compaction rather than fall back to full expansion: the store is mid-teardown +// (or could not be restored to its ended state), so running the full path +// against it is unsafe. Every other error is safe to fall back on — the store +// was untouched or restored, and expanded-grant writes are idempotent. +var errIncrementalFatal = errors.New("incremental expansion: fatal") + +// errIncrementalDroppedEdgeDecline keeps the public revocation contract while +// giving observability a stable, more specific reason. +var errIncrementalDroppedEdgeDecline = fmt.Errorf("%w: dropped edge", expand.ErrIncrementalRevocationDecline) + +// Returns (true, nil) when it handled expansion. Errors come in three shapes: +// decline sentinels (ErrIncrementalFallback for a cycle, +// ErrIncrementalRevocationDecline for a narrowed edge) and plain errors both +// mean "fall back to full expansion" — the store is in the ended state the +// full path expects; errors wrapped in errIncrementalFatal mean the store's +// finalization failed and the compaction must fail. Finalization always runs +// on a detached context so a run-duration timeout can't abort it. +func (c *Compactor) expandGrantsIncremental(ctx context.Context, newSyncId string, compactionStart time.Time) (bool, error) { + // Classification only reads the caller-held graph. Defer the whale-sized + // clone until every cheap decline check passes; chronically ineligible + // inputs should not pay O(graph) memory and CPU before falling back. + base := c.incrementalBaseGraph + if err := base.ValidateCompleted(); err != nil { + return false, fmt.Errorf("incremental expansion: invalid base graph: %w", err) + } + if base.HasCollapsedCycles() { + return false, expand.ErrIncrementalFallback + } + + // Bound the walk by the remaining run duration; the walk polls ctx.Err(). + // Finalization uses detached contexts, so an expired walk deadline never + // aborts the end/cleanup/close. + walkCtx := ctx + if c.runDuration > 0 { + remaining := c.runDuration - time.Since(compactionStart) + if remaining <= 0 { + // Let the full path surface its canonical run-duration error. + return false, fmt.Errorf("incremental expansion: run duration expired before expansion") + } + var cancel context.CancelFunc + walkCtx, cancel = context.WithTimeout(ctx, remaining) + defer cancel() + } + + // The merge left the sync ended; resume it so grants can be written (end + + // close on the way out). Fetch the type first so resume finds the existing sync. + syncResp, err := c.compactedC1z.GetSync(walkCtx, reader_v2.SyncsReaderServiceGetSyncRequest_builder{SyncId: newSyncId}.Build()) + if err != nil { + return false, fmt.Errorf("incremental expansion: get sync: %w", err) + } + syncType := connectorstore.SyncType(syncResp.GetSync().GetSyncType()) + // ResumeSync, never StartOrResumeSync: the merge just produced this sync, so + // a failed lookup is an error, not a cue to start a fresh one. On Pebble, + // StartOrResumeSync's fallback runs ResetForNewSync, which would wipe + // everything the merge wrote. + if _, err := c.compactedC1z.ResumeSync(walkCtx, syncType, newSyncId); err != nil { + return false, fmt.Errorf("incremental expansion: resume sync: %w", err) + } + + // Every rule grant currently in the compacted c1z (base + merged + // increments) contributes to one or more edges. Multiple grants may describe + // different pieces of the SAME edge, so merge their specs before comparing + // them with the base graph. Comparing each piece independently turns an + // unchanged split filter (for example users + groups) into false narrowing. + currentEdges := make(map[[2]string]expand.NewEdge) + sourceEntitlements := make(map[string]*v2.Entitlement) + missingSourceEntitlements := make(map[string]struct{}) + for pe, err := range c.compactedC1z.Grants().PendingExpansion(walkCtx) { + if err != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, fmt.Errorf("incremental expansion: enumerate pending: %w", err) + } + anno := pe.Annotation + if anno == nil { + continue + } + for _, src := range anno.GetEntitlementIds() { + if _, missing := missingSourceEntitlements[src]; missing { + continue + } + sourceEntitlement, cached := sourceEntitlements[src] + if !cached { + resp, getErr := c.compactedC1z.GetEntitlement(walkCtx, + reader_v2.EntitlementsReaderServiceGetEntitlementRequest_builder{ + EntitlementId: src, + }.Build()) + if status.Code(getErr) == codes.NotFound { + missingSourceEntitlements[src] = struct{}{} + ctxzap.Extract(ctx).Debug("incremental expansion: source entitlement not found, skipping edge", + zap.String("src_entitlement_id", src), + zap.String("dst_entitlement_id", pe.TargetEntitlementID)) + continue + } + if getErr != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, fmt.Errorf("incremental expansion: get source entitlement %s: %w", src, getErr) + } + sourceEntitlement = resp.GetEntitlement() + sourceEntitlements[src] = sourceEntitlement + } + + sourceResourceID := sourceEntitlement.GetResource().GetId() + if sourceResourceID == nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, fmt.Errorf("incremental expansion: source entitlement resource id was nil") + } + if pe.PrincipalResourceTypeID != sourceResourceID.GetResourceType() || + pe.PrincipalResourceID != sourceResourceID.GetResource() { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, fmt.Errorf("incremental expansion: source entitlement resource id did not match grant principal id") + } + + curEdge := expand.NewEdge{ + SourceEntitlementID: src, + DestEntitlementID: pe.TargetEntitlementID, + Shallow: anno.GetShallow(), + ResourceTypeIDs: anno.GetResourceTypeIds(), + } + key := [2]string{src, pe.TargetEntitlementID} + if existing, ok := currentEdges[key]; ok { + currentEdges[key] = mergeCurrentEdgeSpecs(existing, curEdge) + } else { + curEdge.ResourceTypeIDs = append([]string(nil), curEdge.ResourceTypeIDs...) + currentEdges[key] = curEdge + } + } + } + + var newEdges []expand.NewEdge + currentBaseNodeEdges := make(map[[2]int]struct{}) + currentEdgeKeys := make([][2]string, 0, len(currentEdges)) + for key := range currentEdges { + currentEdgeKeys = append(currentEdgeKeys, key) + } + sort.Slice(currentEdgeKeys, func(i, j int) bool { + if currentEdgeKeys[i][0] != currentEdgeKeys[j][0] { + return currentEdgeKeys[i][0] < currentEdgeKeys[j][0] + } + return currentEdgeKeys[i][1] < currentEdgeKeys[j][1] + }) + for _, key := range currentEdgeKeys { + curEdge := currentEdges[key] + srcNode := base.GetNode(curEdge.SourceEntitlementID) + dstNode := base.GetNode(curEdge.DestEntitlementID) + if srcNode != nil && dstNode != nil && srcNode.Id != dstNode.Id { + currentBaseNodeEdges[[2]int{srcNode.Id, dstNode.Id}] = struct{}{} + } + baseEdge, inBase := baseGraphEdge(base, curEdge.SourceEntitlementID, curEdge.DestEntitlementID) + if !inBase { + newEdges = append(newEdges, curEdge) // brand-new edge + continue + } + // Existing edge: compare its combined current spec with the combined + // spec persisted in the base graph. + switch classifyEdgeSpecChange(baseEdge, curEdge) { + case edgeSpecNarrowed: + // Revocation-shaped (shallow-ified / filter tightened): can't + // remove grants incrementally — decline via the named hook (#6). + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, expand.ErrIncrementalRevocationDecline + case edgeSpecWidened: + // More members now qualify: re-expand (AddEdge folds the wider + // spec into the graph, deep-wins/unfiltered-wins). + newEdges = append(newEdges, curEdge) + case edgeSpecUnchanged: + // nothing to do + } + } + // PendingExpansion describes the complete current edge set. Check the + // reverse direction too: a base edge missing from current data is a + // revocation-shaped change and cannot be applied incrementally. + for _, edge := range base.Edges { + if _, ok := currentBaseNodeEdges[[2]int{edge.SourceID, edge.DestinationID}]; ok { + continue + } + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, errIncrementalDroppedEdgeDecline + } + + // Changed entitlements are derived from the applied increments (their + // grants' entitlement ids), not supplied by the caller — trust the data. + changedEntitlementIDs, err := c.changedEntitlementIDs(walkCtx) + if err != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, err + } + + if len(newEdges) == 0 && len(changedEntitlementIDs) == 0 { + // Nothing changed relative to the base — its grants were already merged in. + base, err = base.Clone() + if err != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, fmt.Errorf("incremental expansion: clone base graph: %w", err) + } + verification, err := c.runIncrementalInvariants(walkCtx, newSyncId, syncType) + if err != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, err + } + return c.finishIncrementalExpansion(ctx, newSyncId, base, verification) + } + + base, err = base.Clone() + if err != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, fmt.Errorf("incremental expansion: clone base graph: %w", err) + } + incrementalStore := sync.NewExpanderStore(c.compactedC1z) + if c.incrementalTestHook != nil { + incrementalStore = &incrementalFaultStore{ExpanderStore: incrementalStore, hook: c.incrementalTestHook} + } + ie := expand.NewIncrementalExpander(incrementalStore, base) + res, err := ie.ExpandChanges(walkCtx, newEdges, changedEntitlementIDs) + if err != nil { + // Restore the ended state so the full path re-runs against a consistent + // store — for the cycle decline and any real error alike. Writes are + // idempotent by grant identity, so partial progress is safe to re-cover. + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, err // sentinel or plain → caller falls back to full + } + if err := c.runIncrementalTestHook("after_walk"); err != nil { + return false, err + } + + ctxzap.Extract(ctx).Info("incremental grant expansion complete", + zap.Int("entitlements_walked", len(res.EntitlementsWalked)), + zap.Int("grants_written", res.GrantsWritten)) + verification, err := c.runIncrementalInvariants(walkCtx, newSyncId, syncType) + if err != nil { + if endErr := c.restoreEndedSync(ctx); endErr != nil { + return false, endErr + } + return false, err + } + return c.finishIncrementalExpansion(ctx, newSyncId, base, verification) +} + +type incrementalFaultStore struct { + expand.ExpanderStore + hook func(stage string) error + fired bool +} + +func (s *incrementalFaultStore) StoreExpandedGrants(ctx context.Context, grants ...*v2.Grant) error { + if err := s.ExpanderStore.StoreExpandedGrants(ctx, grants...); err != nil { + return err + } + if !s.fired { + s.fired = true + if err := s.hook("mid_expand_write"); err != nil { + return fmt.Errorf("%w: injected failure at mid_expand_write: %w", errIncrementalFatal, err) + } + } + return nil +} + +func (c *Compactor) runIncrementalTestHook(stage string) error { + if c.incrementalTestHook == nil { + return nil + } + if err := c.incrementalTestHook(stage); err != nil { + return fmt.Errorf("%w: injected failure at %s: %w", errIncrementalFatal, stage, err) + } + return nil +} + +func (c *Compactor) runIncrementalInvariants( + ctx context.Context, + syncID string, + syncType connectorstore.SyncType, +) (*c1zstore.IngestInvariantVerification, error) { + if writer, ok := c.compactedC1z.SyncMeta().(c1zstore.IngestInvariantVerificationWriter); ok { + if err := writer.ClearIngestInvariantVerification(ctx, syncID); err != nil { + return nil, fmt.Errorf("incremental expansion: clear invariant verification: %w", err) + } + } + verification, err := sync.RunIngestInvariantsWithVerification(ctx, c.compactedC1z, sync.IngestInvariantsPolicy{ + ActiveSyncID: syncID, + SyncType: syncType, + FailFast: c.failFastInvariants, + CompactionMerge: true, + }) + if err != nil { + return nil, fmt.Errorf("incremental expansion: ingest invariants: %w", err) + } + return verification, nil +} + +// persistGraphSidecar writes the post-expansion graph into the compacted c1z +// so the artifact carries its own base graph for the next incremental run. +// Best-effort: on failure the next run just falls back to full expansion. +func (c *Compactor) persistGraphSidecar(ctx context.Context, g *expand.EntitlementGraph, syncID string) { + gs, ok := c.compactedC1z.(sync.EntitlementGraphStore) + if !ok { + return + } + digestReader, ok := c.compactedC1z.(c1zstore.GrantGenerationDigestReader) + if !ok { + return + } + digest, found, err := digestReader.GrantGenerationDigest(ctx) + if err != nil || !found { + ctxzap.Extract(ctx).Warn("incremental expansion: sealed grant digest unavailable; graph will not be reusable", zap.Error(err)) + return + } + data, err := expand.MarshalGraphBlobWithGrantDigest(syncID, g, digest) + if err == nil { + err = gs.PutEntitlementGraphBlob(ctx, data) + } + if err != nil { + ctxzap.Extract(ctx).Warn("incremental expansion: persist graph sidecar failed", zap.Error(err)) + } +} + +// restoreEndedSync returns the compacted sync to the ended state the full path +// expects, after an incremental attempt that resumed it. Runs on a detached, +// timeout-bounded context so a cancelled parent can't strand the store +// mid-resume. Its failure is FATAL (errIncrementalFatal): the store is in an +// unknown state and the full path must not run against it. +func (c *Compactor) restoreEndedSync(ctx context.Context) error { + endCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), dotc1z.FinalizeTimeout()) + defer cancel() + if err := c.compactedC1z.EndSync(endCtx); err != nil { + return fmt.Errorf("%w: restore ended sync: %w", errIncrementalFatal, err) + } + return nil +} + +// finishIncrementalExpansion ends, cleans up, and closes the store so the file +// is flushed before cpFile copies it — converging with the other compaction +// paths (Cleanup is a Pebble no-op today, kept for parity). Runs on a detached, +// timeout-bounded context so a cancelled or run-duration-expired parent can't +// abort finalization. All errors here are FATAL (errIncrementalFatal): the +// store is being torn down, so falling back to full expansion against it is +// not safe. +func (c *Compactor) finishIncrementalExpansion( + ctx context.Context, + syncID string, + graph *expand.EntitlementGraph, + verification *c1zstore.IngestInvariantVerification, +) (bool, error) { + finalizeCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), dotc1z.FinalizeTimeout()) + defer cancel() + if err := c.compactedC1z.Cleanup(finalizeCtx); err != nil { + return false, fmt.Errorf("%w: cleanup: %w", errIncrementalFatal, err) + } + if err := c.runIncrementalTestHook("before_end_sync"); err != nil { + return false, err + } + if err := c.compactedC1z.EndSync(finalizeCtx); err != nil { + return false, fmt.Errorf("%w: end sync: %w", errIncrementalFatal, err) + } + if err := c.runIncrementalTestHook("after_end_sync"); err != nil { + return false, err + } + c.persistGraphSidecar(finalizeCtx, graph, syncID) + if err := c.runIncrementalTestHook("after_sidecar"); err != nil { + return false, err + } + if verification != nil { + if writer, ok := c.compactedC1z.SyncMeta().(c1zstore.IngestInvariantVerificationWriter); ok { + if err := writer.MarkIngestInvariantsVerified(finalizeCtx, syncID, *verification); err != nil { + ctxzap.Extract(ctx).Warn("incremental expansion: persist invariant verification failed; artifact remains unverified", zap.Error(err)) + } + } + } + if err := c.runIncrementalTestHook("after_marker"); err != nil { + return false, err + } + if err := c.runIncrementalTestHook("before_close"); err != nil { + return false, err + } + if err := c.compactedC1z.Close(finalizeCtx); err != nil { + return false, fmt.Errorf("%w: close: %w", errIncrementalFatal, err) + } + return true, nil +} + +// changedEntitlementIDs returns the entitlement ids whose grants changed in +// the applied increments, seeding the incremental walk. The fold collects +// them during its merge (no re-read, no-ops excluded); rebuild-mode +// compactions fall back to deriveChangedEntitlementIDs. +func (c *Compactor) changedEntitlementIDs(ctx context.Context) ([]string, error) { + if c.foldChangedEntitlementIDs != nil { + out := make([]string, 0, len(c.foldChangedEntitlementIDs)) + for id := range c.foldChangedEntitlementIDs { + out = append(out, id) + } + sort.Strings(out) + return out, nil + } + return c.deriveChangedEntitlementIDs(ctx) +} + +// deriveChangedEntitlementIDs is the no-fold fallback: re-open each increment +// (entries[1:]) and collect its grants' entitlement ids. +func (c *Compactor) deriveChangedEntitlementIDs(ctx context.Context) ([]string, error) { + if len(c.entries) < 2 { + return nil, nil + } + seen := make(map[string]struct{}) + for _, e := range c.entries[1:] { + // Same open options as doOneCompaction: honor the caller's tmp dir + // (extraction must not silently land in os.TempDir()) and parallel decode. + store, err := dotc1z.NewStore(ctx, e.FilePath, + dotc1z.WithTmpDir(c.tmpDir), + dotc1z.WithDecoderOptions(dotc1z.WithDecoderConcurrency(-1)), + dotc1z.WithReadOnly(true), + ) + if err != nil { + return nil, fmt.Errorf("incremental expansion: open increment %s: %w", e.SyncID, err) + } + err = collectGrantEntitlementIDs(ctx, store, e.SyncID, seen) + if closeErr := store.Close(ctx); closeErr != nil && err == nil { + err = fmt.Errorf("incremental expansion: close increment %s: %w", e.SyncID, closeErr) + } + if err != nil { + return nil, err + } + } + out := make([]string, 0, len(seen)) + for id := range seen { + out = append(out, id) + } + sort.Strings(out) + return out, nil +} + +// collectGrantEntitlementIDs adds every entitlement id that has a grant in the +// given sync to seen. +func collectGrantEntitlementIDs(ctx context.Context, store c1zstore.Store, syncID string, seen map[string]struct{}) error { + if err := store.SetCurrentSync(ctx, syncID); err != nil { + return fmt.Errorf("incremental expansion: set increment sync %s: %w", syncID, err) + } + pageToken := "" + for { + resp, err := store.ListGrants(ctx, v2.GrantsServiceListGrantsRequest_builder{ + PageSize: 1000, + PageToken: pageToken, + }.Build()) + if err != nil { + return fmt.Errorf("incremental expansion: list increment grants for %s: %w", syncID, err) + } + for _, g := range resp.GetList() { + if id := g.GetEntitlement().GetId(); id != "" { + seen[id] = struct{}{} + } + } + pageToken = resp.GetNextPageToken() + if pageToken == "" { + return nil + } + } +} + +// baseGraphEdge returns the base graph's edge src->dst and whether one exists. +// Endpoints collapsed into one node (a fixed cycle) count as present with no +// distinct edge (nil), which classifyEdgeSpecChange treats as unchanged. +func baseGraphEdge(g *expand.EntitlementGraph, src, dst string) (*expand.Edge, bool) { + sn := g.GetNode(src) + dn := g.GetNode(dst) + if sn == nil || dn == nil { + return nil, false + } + if sn.Id == dn.Id { + return nil, true + } + dests, ok := g.SourcesToDestinations[sn.Id] + if !ok { + return nil, false + } + edgeID, ok := dests[dn.Id] + if !ok { + return nil, false + } + e, ok := g.Edges[edgeID] + if !ok { + return nil, false + } + return &e, true +} + +// mergeCurrentEdgeSpecs folds parallel connector rules for the same endpoints +// into the one effective graph edge AddEdge would build: deep wins over +// shallow, an unfiltered rule wins over filtered rules, and otherwise filters +// are unioned. +func mergeCurrentEdgeSpecs(left, right expand.NewEdge) expand.NewEdge { + out := left + out.Shallow = left.Shallow && right.Shallow + if len(left.ResourceTypeIDs) == 0 || len(right.ResourceTypeIDs) == 0 { + out.ResourceTypeIDs = nil + return out + } + + resourceTypeIDs := make(map[string]struct{}, len(left.ResourceTypeIDs)+len(right.ResourceTypeIDs)) + for _, id := range left.ResourceTypeIDs { + resourceTypeIDs[id] = struct{}{} + } + for _, id := range right.ResourceTypeIDs { + resourceTypeIDs[id] = struct{}{} + } + out.ResourceTypeIDs = make([]string, 0, len(resourceTypeIDs)) + for id := range resourceTypeIDs { + out.ResourceTypeIDs = append(out.ResourceTypeIDs, id) + } + sort.Strings(out.ResourceTypeIDs) + return out +} + +type edgeSpecChange int + +const ( + edgeSpecUnchanged edgeSpecChange = iota + edgeSpecWidened + edgeSpecNarrowed +) + +// classifyEdgeSpecChange compares an existing base edge's spec to the current +// (increment) spec. Narrowing (deep->shallow, filter tightened) is +// revocation-shaped; widening (shallow->deep, filter broadened) needs +// re-expansion. Any narrowing wins (safest: decline to full). +func classifyEdgeSpecChange(base *expand.Edge, cur expand.NewEdge) edgeSpecChange { + if base == nil { + return edgeSpecUnchanged // collapsed cycle: no distinct edge + } + widened, narrowed := false, false + if base.IsShallow && !cur.Shallow { + widened = true // shallow -> deep + } + if !base.IsShallow && cur.Shallow { + narrowed = true // deep -> shallow + } + rw, rn := compareResourceTypeFilter(base.ResourceTypeIDs, cur.ResourceTypeIDs) + widened = widened || rw + narrowed = narrowed || rn + switch { + case narrowed: + return edgeSpecNarrowed + case widened: + return edgeSpecWidened + default: + return edgeSpecUnchanged + } +} + +// compareResourceTypeFilter compares two principal-type filters where an empty +// filter means "all types" (the widest). Returns whether the current filter is +// wider and/or narrower than the base. +func compareResourceTypeFilter(base, cur []string) (bool, bool) { + var widened, narrowed bool + baseAll := len(base) == 0 + curAll := len(cur) == 0 + switch { + case baseAll && curAll: + return false, false + case baseAll && !curAll: + return false, true // all -> some + case !baseAll && curAll: + return true, false // some -> all + } + baseSet := make(map[string]struct{}, len(base)) + for _, t := range base { + baseSet[t] = struct{}{} + } + curSet := make(map[string]struct{}, len(cur)) + for _, t := range cur { + curSet[t] = struct{}{} + } + for t := range curSet { + if _, ok := baseSet[t]; !ok { + widened = true + } + } + for t := range baseSet { + if _, ok := curSet[t]; !ok { + narrowed = true + } + } + return widened, narrowed +} + func (c *Compactor) expandGrants(ctx context.Context, newSyncId string, compactionStart time.Time) error { l := ctxzap.Extract(ctx) + + // Diff-aware fast path: with a base graph, expand only what changed relative + // to it. Any doubt (cycle, error) falls through to full expansion below. + // Pebble-only: it reopens the ended compacted sync to write grants, which + // only Pebble supports; on other engines we degrade gracefully to full. + switch { + case !c.incrementalExpansion: + logIncrementalOutcome(ctx, "not_attempted", "not_requested") + case c.resolvedEngine() != c1zstore.EnginePebble: + logIncrementalOutcome(ctx, "not_attempted", "unsupported_engine", + zap.String("engine", string(c.resolvedEngine()))) + default: + baseGraph, loadErr := c.loadIncrementalBaseGraph(ctx) + if loadErr != nil { + logIncrementalOutcome(ctx, "fell_back", "base_graph_error", zap.Error(loadErr)) + break + } + if baseGraph == nil { + logIncrementalOutcome(ctx, "fell_back", "base_graph_missing_or_stale") + break + } + c.incrementalBaseGraph = baseGraph + done, err := c.expandGrantsIncremental(ctx, newSyncId, compactionStart) + switch { + case errors.Is(err, errIncrementalFatal): + // The store's finalization (or restore-to-ended) failed: it is in an + // unknown/torn-down state, so running full expansion against it is + // unsafe. Fail the compaction. + logIncrementalOutcome(ctx, "failed", "finalization_error", zap.Error(err)) + return fmt.Errorf("incremental grant expansion: %w", err) + case errors.Is(err, errIncrementalDroppedEdgeDecline): + logIncrementalOutcome(ctx, "declined", "dropped_edge") + case errors.Is(err, expand.ErrIncrementalRevocationDecline): + // Named revocation hook (#6): today declines to full; a future + // tombstone stage flips this one site to apply deletions. + logIncrementalOutcome(ctx, "declined", "revocation") + case errors.Is(err, expand.ErrIncrementalDenseChangeDecline): + logIncrementalOutcome(ctx, "declined", "dense_change") + case errors.Is(err, expand.ErrIncrementalFallback): + // New edge closed a cycle: full expansion handles cycles correctly. + logIncrementalOutcome(ctx, "declined", "cycle") + case err != nil: + // Pre-write or restored-state failure: the store is back in the + // ended state the full path expects, so falling back is safe. + logIncrementalOutcome(ctx, "fell_back", "incremental_error", zap.Error(err)) + case done: + // Incremental path already ended + closed the store; caller clears + // c.compactedC1z after return, same as the full path. + c.incrementalExpansionRan = true + logIncrementalOutcome(ctx, "succeeded", "none") + return nil + } + } + // Grant expansion doesn't use the connector interface at all, so giving syncer an empty connector is safe... for now. // If that ever changes, we should implement a file connector that is a wrapper around the reader. emptyConnector, err := sdk.NewEmptyConnector() @@ -588,6 +1305,23 @@ func (c *Compactor) expandGrants(ctx context.Context, newSyncId string, compacti // pinned by TestCompactionExpandToleratesMergeManufacturedExclusionConflicts. sync.WithCompactionMergedStore(), } + if c.failFastInvariants { + syncOpts = append(syncOpts, sync.WithFailFastInvariants()) + } + + // Keep the artifact's graph sidecar coherent with this full expansion: + // opted-in compactions preserve a fresh graph (so the incremental chain + // heals after a fallback); otherwise drop any sidecar inherited from a + // fold-copied base. Pebble-only: incremental expansion declines on other + // engines, and without a sidecar the preserved graph would only bloat + // the final sync token. + if c.incrementalExpansion && c.resolvedEngine() == c1zstore.EnginePebble { + syncOpts = append(syncOpts, sync.WithPreserveEntitlementGraph()) + } else if gs, ok := c.compactedC1z.(sync.EntitlementGraphStore); ok { + if err := gs.DeleteEntitlementGraphBlob(ctx); err != nil { + l.Warn("expandGrants: delete inherited graph sidecar failed", zap.Error(err)) + } + } compactionDuration := time.Since(compactionStart) runDuration := c.runDuration - compactionDuration @@ -620,3 +1354,53 @@ func (c *Compactor) expandGrants(ctx context.Context, newSyncId string, compacti } return nil } + +func logIncrementalOutcome(ctx context.Context, outcome, reason string, fields ...zap.Field) { + fields = append([]zap.Field{ + zap.String("incremental_expansion_outcome", outcome), + zap.String("incremental_expansion_reason", reason), + }, fields...) + ctxzap.Extract(ctx).Info("incremental grant expansion outcome", fields...) +} + +func (c *Compactor) loadIncrementalBaseGraph(ctx context.Context) (*expand.EntitlementGraph, error) { + if len(c.entries) == 0 || c.entries[0] == nil || c.entries[0].SyncID == "" { + return nil, fmt.Errorf("incremental expansion: compaction base is missing") + } + store, err := dotc1z.NewStore(ctx, c.entries[0].FilePath, + dotc1z.WithReadOnly(true), dotc1z.WithTmpDir(c.tmpDir)) + if err != nil { + return nil, fmt.Errorf("incremental expansion: open base graph store: %w", err) + } + run, runErr := store.SyncMeta().LatestFinishedSyncOfAnyType(ctx) + if runErr != nil { + _ = store.Close(ctx) + return nil, fmt.Errorf("incremental expansion: load base verification: %w", runErr) + } + // Both engines return (nil, nil) when the artifact holds no finished sync + // (e.g. an interrupted collection): decline to full expansion, don't panic. + if run == nil { + _ = store.Close(ctx) + return nil, fmt.Errorf("incremental expansion: base has no finished sync") + } + if run.ID != c.entries[0].SyncID || + !run.IsVerified() || + run.Generation != sync.IngestInvariantGeneration { + _ = store.Close(ctx) + return nil, fmt.Errorf("incremental expansion: base grant generation is not verified") + } + graph, graphErr := sync.GraphFromStore(ctx, store, c.entries[0].SyncID) + closeErr := store.Close(ctx) + if graphErr != nil { + return nil, fmt.Errorf("incremental expansion: load base graph: %w", graphErr) + } + if closeErr != nil { + return nil, fmt.Errorf("incremental expansion: close base graph store: %w", closeErr) + } + if graph != nil { + if err := graph.ValidateCompleted(); err != nil { + return nil, fmt.Errorf("incremental expansion: invalid base graph: %w", err) + } + } + return graph, nil +} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor_pebble.go b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor_pebble.go index c0ee8408..a913bc83 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor_pebble.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/compactor_pebble.go @@ -27,10 +27,11 @@ import ( ) // WithEngine selects the storage engine for the compacted output. -// The default (unset) is sqlite, which is byte-identical to the -// historical compactor. EnginePebble produces a v3 Pebble c1z via a -// native record merge whose strategy (overlay / fold / kway) is -// resolved per run by resolvePebbleMode. +// The default (unset) follows the inputs — any Pebble input makes the +// output Pebble; all-SQLite inputs keep SQLite output, byte-identical +// to the historical compactor (see inferEngineFromInputs). EnginePebble +// produces a v3 Pebble c1z via a native record merge whose strategy +// (overlay / fold / kway) is resolved per run by resolvePebbleMode. // // This is the only supported way to choose the engine; an engine // passed through WithC1ZOptions does not select the compaction @@ -348,9 +349,9 @@ func ensurePebbleRegistered() error { } // compactableV3SyncType reports whether a v3 sync type is a compactable -// snapshot type. Diff syncs (partial_upserts / partial_deletions) are -// excluded — compaction folds full / resources-only / partial snapshots -// only, matching the sqlite source selection. +// snapshot type. Compaction folds full / resources-only / partial +// snapshots only (never unspecified/unknown types), matching the sqlite +// source selection. func compactableV3SyncType(t v3.SyncType) bool { switch t { case v3.SyncType_SYNC_TYPE_FULL, @@ -444,8 +445,8 @@ func selectSourceSyncFromManifest(path string) (manifestSourceSelection, bool) { // - Base primary and index keys: zero writes — the data keyspace // carries no sync_id, so folding and the final rename touch none of // them. Work is O(partials), not O(base). -// - Partial winners are merged into the base keyspace via the -// engine's keep-newer path (Put*RecordsIfNewer), which compares +// - Partial winners are merged into the base keyspace via the raw +// keep-newer merge (mergeBucketRawIfNewer), which compares // discovered_at against the incumbent and maintains indexes with // point tombstones proportional to overridden records only. // - Tie semantics: a partial record with discovered_at EQUAL to the @@ -492,7 +493,7 @@ func (c *Compactor) compactPebbleFold(ctx context.Context) (string, error) { var convertedInputs []string defer func() { for _, path := range convertedInputs { - _ = os.Remove(path) + _ = os.Remove(path) // #nosec G703 -- paths come only from CreateTemp in the compactor temp directory. } }() for i := len(c.entries) - 1; i >= 1; i-- { @@ -549,7 +550,11 @@ func (c *Compactor) compactPebbleFold(ctx context.Context) (string, error) { partialSyncIDs = append(partialSyncIDs, srcSyncID) partialTokens = append(partialTokens, readSourceSyncToken(ctx, srcEng, srcSyncID)) - mergeStats, mergeErr := mergepkg.MergeInto(ctx, destEng, []mergepkg.SourceSync{{Engine: srcEng, SyncID: srcSyncID}}, baseSyncID) + var mergeOpts []mergepkg.MergeOption + if c.incrementalExpansion { + mergeOpts = append(mergeOpts, mergepkg.WithGrantEntitlementIDs()) + } + mergeStats, mergeErr := mergepkg.MergeInto(ctx, destEng, []mergepkg.SourceSync{{Engine: srcEng, SyncID: srcSyncID}}, baseSyncID, mergeOpts...) foldStats.Add(mergeStats) if cerr := w.Close(ctx); cerr != nil { l.Error("compactPebbleFold: error closing source store", zap.Error(cerr), zap.String("file", sourcePath)) @@ -559,6 +564,15 @@ func (c *Compactor) compactPebbleFold(ctx context.Context) (string, error) { } } + if c.incrementalExpansion { + // Hand the fold's changed-entitlement set to incremental expansion. + // Non-nil even when empty: nil means "no fold ran" (derive fallback). + c.foldChangedEntitlementIDs = foldStats.GrantEntitlementIDs + if c.foldChangedEntitlementIDs == nil { + c.foldChangedEntitlementIDs = map[string]struct{}{} + } + } + // Record the bytes this fold shadowed in the base keyspace. The // store inherited the base manifest's running fold_dead_bytes at // open (the dest is a byte copy of the base), so adding the delta @@ -677,6 +691,13 @@ func (c *Compactor) compactPebbleFold(ctx context.Context) (string, error) { // a lineage link would dangle, and the rebuild path's compacted // output carries no parent either. newSyncID := ksuid.New().String() + // The folded store is copied from the base, but the graph sidecar is + // stamped with that base sync ID and may no longer describe merged data. + // Drop it before publishing the fresh sync; a following expansion writes a + // new graph, while skip-expansion artifacts safely fall back next time. + if err := destEng.DeleteEntitlementGraphSidecar(ctx); err != nil { + return "", fmt.Errorf("compactPebbleFold: delete inherited entitlement graph: %w", err) + } baseRec.SetSyncId(newSyncID) baseRec.SetParentSyncId("") baseRec.SetType(unionType) @@ -881,7 +902,7 @@ func copyFileForFold(src, dst string) error { // readCompactionInputFormat reads the c1z header of path and returns its // on-disk format, rejecting anything that is not a supported v1/v3 c1z. func readCompactionInputFormat(path string) (dotc1z.C1ZFormat, error) { - f, err := os.Open(path) // #nosec G304 - compaction inputs are caller-provided c1z paths. + f, err := os.Open(path) // #nosec G304,G703 -- compaction inputs are intentionally caller-provided c1z paths. if err != nil { return dotc1z.C1ZFormatUnknown, fmt.Errorf("compactPebble: open input header %s: %w", path, err) } @@ -947,11 +968,7 @@ func resolveSQLiteCompactionSyncID(ctx context.Context, store *dotc1z.C1File, ex } } if best == nil { - return "", fmt.Errorf( - "no finished compactable sync found in sqlite input (diff sync types %q/%q are not compactable)", - string(connectorstore.SyncTypePartialUpserts), - string(connectorstore.SyncTypePartialDeletions), - ) + return "", fmt.Errorf("no finished compactable sync found in sqlite input") } return best.GetId(), nil } @@ -1000,11 +1017,11 @@ func (c *Compactor) convertSQLiteInputToPebble(ctx context.Context, cs *Compacta } convertedPath := tmp.Name() if err := tmp.Close(); err != nil { - _ = os.Remove(convertedPath) + _ = os.Remove(convertedPath) // #nosec G703 -- convertedPath was returned by CreateTemp above. return "", fmt.Errorf("compactPebble: close conversion temp file: %w", err) } // ToPebble requires the destination path to not exist. - if err := os.Remove(convertedPath); err != nil { + if err := os.Remove(convertedPath); err != nil { // #nosec G703 -- convertedPath was returned by CreateTemp above. return "", fmt.Errorf("compactPebble: remove conversion temp placeholder: %w", err) } @@ -1020,16 +1037,16 @@ func (c *Compactor) convertSQLiteInputToPebble(ctx context.Context, cs *Compacta syncID, err := resolveSQLiteCompactionSyncID(ctx, sqliteStore, cs.SyncID) if err != nil { _ = store.Close(ctx) - _ = os.Remove(convertedPath) + _ = os.Remove(convertedPath) // #nosec G703 -- convertedPath was returned by CreateTemp above. return "", fmt.Errorf("compactPebble: select sqlite input sync %s: %w", cs.FilePath, err) } if _, err := sqliteStore.ToPebble(ctx, convertedPath, syncID, dotc1z.WithConvertTmpDir(c.tmpDir)); err != nil { _ = store.Close(ctx) - _ = os.Remove(convertedPath) + _ = os.Remove(convertedPath) // #nosec G703 -- convertedPath was returned by CreateTemp above. return "", fmt.Errorf("compactPebble: convert sqlite input %s to pebble: %w", cs.FilePath, err) } if err := store.Close(ctx); err != nil { - _ = os.Remove(convertedPath) + _ = os.Remove(convertedPath) // #nosec G703 -- convertedPath was returned by CreateTemp above. return "", fmt.Errorf("compactPebble: close sqlite input after conversion %s: %w", cs.FilePath, err) } return convertedPath, nil @@ -1037,8 +1054,8 @@ func (c *Compactor) convertSQLiteInputToPebble(ctx context.Context, cs *Compacta // compactPebble folds every input into the empty newSyncId on the // Pebble output via a native record merge: each input is opened, its -// latest finished compactable sync is selected (diff syncs excluded), -// and all are merged keeping the newest record per key. The output +// latest finished compactable sync is selected, and all are merged +// keeping the newest record per key. The output // sync_run's type and ended_at are then set to the union / max across // the inputs (mirroring the sqlite UpdateSync), and its stats are // recomputed. Inputs are merged in reverse entry order so the tie @@ -1088,7 +1105,7 @@ func (c *Compactor) compactPebble(ctx context.Context, newSyncId string) error { var convertedInputs []string defer func() { for _, path := range convertedInputs { - _ = os.Remove(path) + _ = os.Remove(path) // #nosec G703 -- paths come only from CreateTemp in the compactor temp directory. } }() for i := len(c.entries) - 1; i >= 0; i-- { @@ -1145,7 +1162,7 @@ func (c *Compactor) compactPebble(ctx context.Context, newSyncId string) error { return zeroSource, v3.SyncType_SYNC_TYPE_UNSPECIFIED, time.Time{}, fmt.Errorf("compactPebble: select source sync for %s: %w", sourcePath, err) } if rec == nil { - return zeroSource, v3.SyncType_SYNC_TYPE_UNSPECIFIED, time.Time{}, fmt.Errorf("compactPebble: input %s has no finished compactable sync (diff syncs are not compactable)", sourcePath) + return zeroSource, v3.SyncType_SYNC_TYPE_UNSPECIFIED, time.Time{}, fmt.Errorf("compactPebble: input %s has no finished compactable sync", sourcePath) } // Record only (Path, SyncID, Stats) and fully close the store, diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/doc.go b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/doc.go index 36c98a2b..fc0e4f8e 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/doc.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/doc.go @@ -55,9 +55,9 @@ // // Not a rebuild. The dest store starts as a byte copy of the base // input, the output adopts the base sync's id, and each partial's -// records are streamed into the base keyspace through the engine's -// keep-newer puts (Put*RecordsIfNewer), which resolve conflicts -// against incumbents by discovered_at and maintain indexes with point +// records are streamed into the base keyspace through the raw +// keep-newer merge (mergeBucketRawIfNewer), which resolves conflicts +// against incumbents by discovered_at and maintains indexes with point // tombstones for overridden records only. Base records are never // read, decoded, or rewritten, and the envelope save splices the // base's unchanged zstd frames instead of re-encoding them — total diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/merge.go b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/merge.go index 627ee905..2308c419 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/merge.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/synccompactor/pebble/merge.go @@ -18,6 +18,23 @@ type SourceSync struct { SyncID string } +type mergeOptions struct { + collectGrantEntitlementIDs bool +} + +// MergeOption enables optional MergeInto behavior without breaking callers +// that use the original four-argument API. +type MergeOption func(*mergeOptions) + +// WithGrantEntitlementIDs records the entitlement IDs of grant records that +// MergeInto actually writes. The incremental expander uses these IDs as its +// changed-node seeds. +func WithGrantEntitlementIDs() MergeOption { + return func(opts *mergeOptions) { + opts.collectGrantEntitlementIDs = true + } +} + // FoldStats reports what a MergeInto call overrode in the destination // keyspace. DeadBytes is the exact raw size (keys + values) of the // incumbent records — and their derived index keys — that the fold @@ -56,6 +73,9 @@ type FoldStats struct { // (Engine.InvalidateGrantDigestPartitions + // Engine.RepairMissingGrantDigests), instead of the whole file. TouchedGrantPartitions map[string]struct{} + // GrantEntitlementIDs: distinct entitlement ids of applied grant records + // (no-ops excluded). Seeds incremental expansion without re-reading inputs. + GrantEntitlementIDs map[string]struct{} } func (s *FoldStats) Add(o FoldStats) { @@ -74,6 +94,24 @@ func (s *FoldStats) Add(o FoldStats) { } s.TouchedGrantPartitions[p] = struct{}{} } + for id := range o.GrantEntitlementIDs { + s.noteGrantEntitlementID([]byte(id)) + } +} + +// noteGrantEntitlementID records one applied grant's entitlement id; +// read-before-insert keeps repeats allocation-free. +func (s *FoldStats) noteGrantEntitlementID(id []byte) { + if len(id) == 0 { + return + } + if _, ok := s.GrantEntitlementIDs[string(id)]; ok { + return + } + if s.GrantEntitlementIDs == nil { + s.GrantEntitlementIDs = make(map[string]struct{}) + } + s.GrantEntitlementIDs[string(id)] = struct{}{} } func (s *FoldStats) bumpAdded(bucket string, n int64) { @@ -136,8 +174,14 @@ func (s *FoldStats) bumpReplaced(bucket string, n int64) { // (Engine.BuildGrantDigests rebuilds both keyspaces atomically from // scratch, so no separate drop is needed even then — see // compactPebbleFold). -func MergeInto(ctx context.Context, dest *enginepkg.Engine, sources []SourceSync, destSyncID string) (FoldStats, error) { +func MergeInto(ctx context.Context, dest *enginepkg.Engine, sources []SourceSync, destSyncID string, options ...MergeOption) (FoldStats, error) { var stats FoldStats + opts := mergeOptions{} + for _, option := range options { + if option != nil { + option(&opts) + } + } if dest == nil { return stats, errors.New("synccompactor/pebble.MergeInto: dest engine is nil") } @@ -159,7 +203,7 @@ func MergeInto(ctx context.Context, dest *enginepkg.Engine, sources []SourceSync if s.Engine == nil || s.SyncID == "" { continue } - srcStats, err := mergeOneSource(ctx, dest, s, destSyncID) + srcStats, err := mergeOneSource(ctx, dest, s, destSyncID, opts.collectGrantEntitlementIDs) stats.Add(srcStats) if err != nil { return stats, fmt.Errorf("merge source %s: %w", s.SyncID, err) @@ -187,13 +231,13 @@ const mergeRawFlushRecords = 32768 // newer wins, replacing the value and swapping the incumbent's // derived index keys for the new value's (point deletes // proportional to overridden records only). Ties keep the -// incumbent, mirroring the engine's Put*RecordsIfNewer rule — -// missing discovered_at scans as 0, reproducing its nil-timestamp -// ordering ("never overwrite an incumbent, always fill a hole"). -func mergeOneSource(ctx context.Context, dest *enginepkg.Engine, s SourceSync, destSyncID string) (FoldStats, error) { +// incumbent — missing discovered_at scans as 0, giving +// nil-timestamp ordering ("never overwrite an incumbent, always +// fill a hole"). +func mergeOneSource(ctx context.Context, dest *enginepkg.Engine, s SourceSync, destSyncID string, collectGrantEntitlementIDs bool) (FoldStats, error) { var stats FoldStats for _, bucket := range allBuckets() { - bucketStats, err := mergeBucketRawIfNewer(ctx, dest, s.Engine, bucket) + bucketStats, err := mergeBucketRawIfNewer(ctx, dest, s.Engine, bucket, collectGrantEntitlementIDs) stats.Add(bucketStats) if err != nil { return stats, fmt.Errorf("merge %s: %w", bucket.name, err) @@ -202,7 +246,7 @@ func mergeOneSource(ctx context.Context, dest *enginepkg.Engine, s SourceSync, d return stats, nil } -func mergeBucketRawIfNewer(ctx context.Context, dest *enginepkg.Engine, src *enginepkg.Engine, bucket bucketSpec) (FoldStats, error) { +func mergeBucketRawIfNewer(ctx context.Context, dest *enginepkg.Engine, src *enginepkg.Engine, bucket bucketSpec, collectGrantEntitlementIDs bool) (FoldStats, error) { var stats FoldStats lower, upper := bucket.syncRange() iter, err := src.NewIter(&pebble.IterOptions{LowerBound: lower, UpperBound: upper}) @@ -287,6 +331,9 @@ func mergeBucketRawIfNewer(ctx context.Context, dest *enginepkg.Engine, src *eng if err := batch.Set(key, value); err != nil { return stats, err } + // Applied grant (skips continued above): count it toward the + // digest-repair signal and collect its entitlement id for + // incremental expansion — both during the read the fold already does. if bucket.id == runBucketGrants { stats.GrantWrites++ if partition, ok := enginepkg.GrantPartitionFromPrimaryKey(key); ok { @@ -295,6 +342,13 @@ func mergeBucketRawIfNewer(ctx context.Context, dest *enginepkg.Engine, src *eng } stats.TouchedGrantPartitions[partition] = struct{}{} } + if collectGrantEntitlementIDs { + _, _, entID, _, _, _, scanErr := scanGrantIndexFieldsBytes(value) + if scanErr != nil { + return stats, scanErr + } + stats.noteGrantEntitlementID(entID) + } } if err := forEachIndexKeyFromRaw(bucket, key, lower, value, &scratch, nil, setIndexKey); err != nil { return stats, err diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/differ.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/differ.go deleted file mode 100644 index c1d76e93..00000000 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/local/differ.go +++ /dev/null @@ -1,84 +0,0 @@ -package local - -import ( - "context" - "errors" - "sync" - "time" - - v1 "github.com/conductorone/baton-sdk/pb/c1/connectorapi/baton/v1" - "github.com/conductorone/baton-sdk/pkg/dotc1z" - "github.com/conductorone/baton-sdk/pkg/tasks" - "github.com/conductorone/baton-sdk/pkg/types" - "github.com/conductorone/baton-sdk/pkg/uotel" - "github.com/conductorone/baton-sdk/pkg/uotel/uotelzap" - "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" - "go.opentelemetry.io/otel/trace" - "go.uber.org/zap" -) - -type localDiffer struct { - dbPath string - o sync.Once - - baseSyncID string - appliedSyncID string -} - -func (m *localDiffer) GetTempDir() string { - return "" -} - -func (m *localDiffer) ShouldDebug() bool { - return false -} - -func (m *localDiffer) Next(ctx context.Context) (*v1.Task, time.Duration, error) { - var task *v1.Task - m.o.Do(func() { - task = v1.Task_builder{ - CreateSyncDiff: &v1.Task_CreateSyncDiffTask{}, - }.Build() - }) - return task, 0, nil -} - -func (m *localDiffer) Process(ctx context.Context, task *v1.Task, cc types.ConnectorClient) error { - ctx, span := tracer.Start(ctx, "localDiffer.Process", trace.WithNewRoot()) - ctx = uotelzap.WithSpanLogFields(ctx) - var err error - defer func() { uotel.EndSpanWithError(span, err) }() - log := ctxzap.Extract(ctx) - - if m.baseSyncID == "" || m.appliedSyncID == "" { - return errors.New("missing base sync ID or applied sync ID") - } - - file, err := dotc1z.NewStore(ctx, m.dbPath) - if err != nil { - return err - } - - newSyncID, err := file.FileOps().GenerateSyncDiff(ctx, m.baseSyncID, m.appliedSyncID) - if err != nil { - return err - } - - if err := file.Close(ctx); err != nil { - log.Error("failed to close store", zap.Error(err)) - return err - } - - log.Info("generated diff of syncs", zap.String("new_sync_id", newSyncID)) - - return nil -} - -// NewDiffer returns a task manager that queues a revoke task. -func NewDiffer(ctx context.Context, dbPath string, baseSyncID string, appliedSyncID string) tasks.Manager { - return &localDiffer{ - dbPath: dbPath, - baseSyncID: baseSyncID, - appliedSyncID: appliedSyncID, - } -} diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go index 73294fa0..ac7d6565 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/tasks/tasks.go @@ -68,8 +68,6 @@ func Is(task *v1.Task, target taskTypes.TaskType) bool { return actualType == v1.Task_ActionInvoke_case case taskTypes.ActionStatusType: return actualType == v1.Task_ActionStatus_case - case taskTypes.CreateSyncDiff: - return actualType == v1.Task_CreateSyncDiff_case case taskTypes.ListEventFeedsType: return actualType == v1.Task_ListEventFeeds_case case taskTypes.ListEventsType: @@ -125,8 +123,6 @@ func GetType(task *v1.Task) taskTypes.TaskType { return taskTypes.ActionInvokeType case v1.Task_ActionStatus_case: return taskTypes.ActionStatusType - case v1.Task_CreateSyncDiff_case: - return taskTypes.CreateSyncDiff case v1.Task_ListEventFeeds_case: return taskTypes.ListEventFeedsType case v1.Task_ListEvents_case: diff --git a/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go b/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go index e35d82cf..6a54c04c 100644 --- a/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go +++ b/vendor/github.com/conductorone/baton-sdk/pkg/types/tasks/tasks.go @@ -66,8 +66,6 @@ func (tt TaskType) String() string { return "invoke_action" case ActionStatusType: return "action_status" - case CreateSyncDiff: - return "create_sync_diff" default: return "unknown" } @@ -104,7 +102,7 @@ const ( ActionGetSchemaType ActionInvokeType ActionStatusType - CreateSyncDiff + _ // was CreateSyncDiff; placeholder pins the ordinals below to their released values ListStaticEntitlementsType IssueCredentialType ) diff --git a/vendor/modules.txt b/vendor/modules.txt index cdf4d692..40904e35 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -272,7 +272,7 @@ github.com/cockroachdb/swiss # github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 ## explicit; go 1.19 github.com/cockroachdb/tokenbucket -# github.com/conductorone/baton-sdk v0.24.6 +# github.com/conductorone/baton-sdk v0.25.1-0.20260825204020-991ca45253a7 ## explicit; go 1.25.2 github.com/conductorone/baton-sdk/internal/connector github.com/conductorone/baton-sdk/pb/c1/c1z/v1 @@ -306,6 +306,7 @@ github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/codec github.com/conductorone/baton-sdk/pkg/dotc1z/engine/pebble/internal/rawdb github.com/conductorone/baton-sdk/pkg/dotc1z/format/v3 +github.com/conductorone/baton-sdk/pkg/exit github.com/conductorone/baton-sdk/pkg/field github.com/conductorone/baton-sdk/pkg/healthcheck github.com/conductorone/baton-sdk/pkg/lambda/grpc