= ({ onClose }) => {
+ const { projectId } = useDatastoreFormContext();
+ const { getUpstashIntegrations } = useUpstash();
+ const { openAuthWindow } = useAuthWindow({
+ authUrl: `/api/projects/${projectId}/oauth/upstash`,
+ });
+
+ const upstashIntegrationsResp = useQuery(
+ ["getUpstashIntegrations", projectId],
+ async () => {
+ const integrations = await getUpstashIntegrations({
+ projectId,
+ });
+ return integrations;
+ },
+ {
+ enabled: !!projectId,
+ refetchInterval: 1000,
+ }
+ );
+ useEffect(() => {
+ if (
+ upstashIntegrationsResp.isSuccess &&
+ upstashIntegrationsResp.data.length > 0
+ ) {
+ onClose();
+ }
+ }, [upstashIntegrationsResp]);
+
+ return (
+
+ Integrate Upstash
+
+
+ To continue, you must authenticate with Upstash.{" "}
+
+ Authorize Porter to create Upstash datastores on your behalf
+
+
+
+ );
+};
diff --git a/dashboard/src/main/home/database-dashboard/tabs/SettingsTab.tsx b/dashboard/src/main/home/database-dashboard/tabs/SettingsTab.tsx
index ba10549d46..d2cd77ed26 100644
--- a/dashboard/src/main/home/database-dashboard/tabs/SettingsTab.tsx
+++ b/dashboard/src/main/home/database-dashboard/tabs/SettingsTab.tsx
@@ -22,7 +22,6 @@ const SettingsTab: React.FC = () => {
useState(false);
const { datastore } = useDatastoreContext();
- const { deleteDatastore } = useDatastore();
return (
@@ -49,9 +48,6 @@ const SettingsTab: React.FC = () => {
onClose={() => {
setShowDeleteDatastoreModal(false);
}}
- onSubmit={async () => {
- await deleteDatastore(datastore.name);
- }}
/>
)}
@@ -61,15 +57,14 @@ const SettingsTab: React.FC = () => {
export default SettingsTab;
type DeleteDatastoreModalProps = {
- onSubmit: () => Promise;
onClose: () => void;
};
-const DeleteDatastoreModal: React.FC = ({
- onSubmit,
+export const DeleteDatastoreModal: React.FC = ({
onClose,
}) => {
const { datastore } = useDatastoreContext();
+ const { deleteDatastore } = useDatastore();
const [inputtedDatastoreName, setInputtedDatastoreName] =
useState("");
@@ -79,7 +74,7 @@ const DeleteDatastoreModal: React.FC = ({
const confirmDeletion = async (): Promise => {
setIsSubmitting(true);
try {
- await onSubmit();
+ await deleteDatastore(datastore.name);
onClose();
} catch (err) {
setDeleteDatastoreError(
diff --git a/dashboard/src/shared/api.tsx b/dashboard/src/shared/api.tsx
index a8f19ccc25..da73c5a746 100644
--- a/dashboard/src/shared/api.tsx
+++ b/dashboard/src/shared/api.tsx
@@ -2068,6 +2068,13 @@ const getNeonIntegrations = baseApi<{}, { projectId: number }>(
}
);
+const getUpstashIntegrations = baseApi<{}, { projectId: number }>(
+ "GET",
+ ({ projectId }) => {
+ return `/api/projects/${projectId}/upstash-integrations`;
+ }
+);
+
const getRevisions = baseApi<
{},
{ id: number; cluster_id: number; namespace: string; name: string }
@@ -2889,7 +2896,13 @@ const getDatastoreCredential = baseApi<
const updateDatastore = baseApi<
{
name: string;
- type: "RDS" | "ELASTICACHE" | "MANAGED-POSTGRES" | "MANAGED-REDIS" | "NEON";
+ type:
+ | "RDS"
+ | "ELASTICACHE"
+ | "MANAGED-POSTGRES"
+ | "MANAGED-REDIS"
+ | "NEON"
+ | "UPSTASH";
engine: "POSTGRES" | "AURORA-POSTGRES" | "REDIS";
values: any;
@@ -3895,6 +3908,7 @@ export default {
getRepoIntegrations,
getSlackIntegrations,
getNeonIntegrations,
+ getUpstashIntegrations,
getRepos,
getRevisions,
getTemplateInfo,
diff --git a/go.mod b/go.mod
index 2f2a217e05..353d86277b 100644
--- a/go.mod
+++ b/go.mod
@@ -88,7 +88,7 @@ require (
github.com/nats-io/nats.go v1.24.0
github.com/open-policy-agent/opa v0.44.0
github.com/ory/client-go v1.9.0
- github.com/porter-dev/api-contracts v0.2.159
+ github.com/porter-dev/api-contracts v0.2.161
github.com/riandyrn/otelchi v0.5.1
github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
diff --git a/go.sum b/go.sum
index f5e65fd22f..a029f74ff8 100644
--- a/go.sum
+++ b/go.sum
@@ -1565,6 +1565,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
github.com/porter-dev/api-contracts v0.2.159 h1:Ze4K0rm8p6sRMxaFW4Nb3dJuzz4NEMQ+UMXMtOKKRQ4=
github.com/porter-dev/api-contracts v0.2.159/go.mod h1:VV5BzXd02ZdbWIPLVP+PX3GKawJSGQnxorVT2sUZALU=
+github.com/porter-dev/api-contracts v0.2.161 h1:kf1ZcS1032eLabBzjwDs9SVcecXwUxJ2mJUkRl9C8jk=
+github.com/porter-dev/api-contracts v0.2.161/go.mod h1:VV5BzXd02ZdbWIPLVP+PX3GKawJSGQnxorVT2sUZALU=
github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
diff --git a/internal/repository/gorm/upstash.go b/internal/repository/gorm/upstash.go
index 2e39b5b67a..34b3b04192 100644
--- a/internal/repository/gorm/upstash.go
+++ b/internal/repository/gorm/upstash.go
@@ -42,6 +42,31 @@ func (repo *UpstashIntegrationRepository) Insert(
return created, nil
}
+// Integrations returns all upstash integrations for a given project
+func (repo *UpstashIntegrationRepository) Integrations(
+ ctx context.Context, projectID uint,
+) ([]ints.UpstashIntegration, error) {
+ ctx, span := telemetry.NewSpan(ctx, "gorm-list-upstash-integrations")
+ defer span.End()
+
+ var integrations []ints.UpstashIntegration
+
+ if err := repo.db.Where("project_id = ?", projectID).Find(&integrations).Error; err != nil {
+ return integrations, telemetry.Error(ctx, span, err, "failed to list upstash integrations")
+ }
+
+ for i, integration := range integrations {
+ decrypted, err := repo.DecryptUpstashIntegration(integration, repo.key)
+ if err != nil {
+ return integrations, telemetry.Error(ctx, span, err, "failed to decrypt")
+ }
+
+ integrations[i] = decrypted
+ }
+
+ return integrations, nil
+}
+
// EncryptUpstashIntegration will encrypt the upstash integration data before
// writing to the DB
func (repo *UpstashIntegrationRepository) EncryptUpstashIntegration(
@@ -88,3 +113,50 @@ func (repo *UpstashIntegrationRepository) EncryptUpstashIntegration(
return encrypted, nil
}
+
+// DecryptUpstashIntegration will decrypt the upstash integration data before
+// returning it from the DB
+func (repo *UpstashIntegrationRepository) DecryptUpstashIntegration(
+ upstashInt ints.UpstashIntegration,
+ key *[32]byte,
+) (ints.UpstashIntegration, error) {
+ decrypted := upstashInt
+
+ if len(decrypted.ClientID) > 0 {
+ plaintext, err := encryption.Decrypt(decrypted.ClientID, key)
+ if err != nil {
+ return decrypted, err
+ }
+
+ decrypted.ClientID = plaintext
+ }
+
+ if len(decrypted.AccessToken) > 0 {
+ plaintext, err := encryption.Decrypt(decrypted.AccessToken, key)
+ if err != nil {
+ return decrypted, err
+ }
+
+ decrypted.AccessToken = plaintext
+ }
+
+ if len(decrypted.RefreshToken) > 0 {
+ plaintext, err := encryption.Decrypt(decrypted.RefreshToken, key)
+ if err != nil {
+ return decrypted, err
+ }
+
+ decrypted.RefreshToken = plaintext
+ }
+
+ if len(decrypted.DeveloperApiKey) > 0 {
+ plaintext, err := encryption.Decrypt(decrypted.DeveloperApiKey, key)
+ if err != nil {
+ return decrypted, err
+ }
+
+ decrypted.DeveloperApiKey = plaintext
+ }
+
+ return decrypted, nil
+}
diff --git a/internal/repository/test/upstash.go b/internal/repository/test/upstash.go
index ed189fe39f..295238f0e0 100644
--- a/internal/repository/test/upstash.go
+++ b/internal/repository/test/upstash.go
@@ -16,3 +16,7 @@ func NewUpstashIntegrationRepository(canQuery bool) repository.UpstashIntegratio
func (s *UpstashIntegrationRepository) Insert(ctx context.Context, upstashInt ints.UpstashIntegration) (ints.UpstashIntegration, error) {
panic("not implemented") // TODO: Implement
}
+
+func (s *UpstashIntegrationRepository) Integrations(ctx context.Context, projectID uint) ([]ints.UpstashIntegration, error) {
+ panic("not implemented") // TODO: Implement
+}
diff --git a/internal/repository/upstash.go b/internal/repository/upstash.go
index 18b4629579..56fc00ef16 100644
--- a/internal/repository/upstash.go
+++ b/internal/repository/upstash.go
@@ -10,4 +10,6 @@ import (
type UpstashIntegrationRepository interface {
// Insert creates a new upstash integration
Insert(ctx context.Context, upstashInt ints.UpstashIntegration) (ints.UpstashIntegration, error)
+ // Integrations returns all upstash integrations belonging to a project
+ Integrations(ctx context.Context, projectID uint) ([]ints.UpstashIntegration, error)
}