Skip to content

Commit a67ad76

Browse files
authored
reorganize preview form under tabs (#4090)
1 parent d2dd07c commit a67ad76

File tree

9 files changed

+293
-276
lines changed

9 files changed

+293
-276
lines changed

api/server/handlers/porter_app/create_app_template.go

Lines changed: 63 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"context"
55
"encoding/base64"
66
"net/http"
7-
"time"
87

8+
"connectrpc.com/connect"
99
"github.com/google/uuid"
1010
"github.com/porter-dev/api-contracts/generated/go/helpers"
1111
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
@@ -41,18 +41,25 @@ func NewCreateAppTemplateHandler(
4141
}
4242
}
4343

44+
// Base64AddonWithEnvVars is a struct that contains a base64 encoded addon proto and its env vars
45+
// These env vars will be used to create an env group that is attached to the addon
46+
type Base64AddonWithEnvVars struct {
47+
Base64Addon string `json:"base64_addon"`
48+
Variables map[string]string `json:"variables"`
49+
Secrets map[string]string `json:"secrets"`
50+
}
51+
4452
// CreateAppTemplateRequest is the request object for the /app-template POST endpoint
4553
type CreateAppTemplateRequest struct {
46-
B64AppProto string `json:"b64_app_proto"`
47-
Variables map[string]string `json:"variables"`
48-
Secrets map[string]string `json:"secrets"`
49-
BaseDeploymentTargetID string `json:"base_deployment_target_id"`
54+
B64AppProto string `json:"b64_app_proto"`
55+
Variables map[string]string `json:"variables"`
56+
Secrets map[string]string `json:"secrets"`
57+
BaseDeploymentTargetID string `json:"base_deployment_target_id"`
58+
Addons []Base64AddonWithEnvVars `json:"addons"`
5059
}
5160

5261
// CreateAppTemplateResponse is the response object for the /app-template POST endpoint
53-
type CreateAppTemplateResponse struct {
54-
AppTemplateID string `json:"app_template_id"`
55-
}
62+
type CreateAppTemplateResponse struct{}
5663

5764
// ServeHTTP creates or updates an app template for a given porter app
5865
func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -108,115 +115,60 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
108115
return
109116
}
110117

111-
porterApps, err := c.Repo().PorterApp().ReadPorterAppsByProjectIDAndName(project.ID, appName)
112-
if err != nil {
113-
err := telemetry.Error(ctx, span, err, "error getting porter app from repo")
114-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
115-
return
116-
}
117-
if len(porterApps) == 0 {
118-
err := telemetry.Error(ctx, span, err, "no porter apps returned")
119-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
120-
return
121-
}
122-
if len(porterApps) > 1 {
123-
err := telemetry.Error(ctx, span, err, "multiple porter apps returned; unable to determine which one to use")
124-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
125-
return
126-
}
127-
128-
if porterApps[0].ID == 0 {
129-
err := telemetry.Error(ctx, span, err, "porter app id is missing")
130-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
131-
return
132-
}
133-
134-
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "porter-app-id", Value: porterApps[0].ID})
135-
136-
var appTemplate *models.AppTemplate
137-
138-
existingAppTemplate, err := c.Repo().AppTemplate().AppTemplateByPorterAppID(
139-
project.ID,
140-
porterApps[0].ID,
141-
)
142-
if err != nil {
143-
err := telemetry.Error(ctx, span, err, "error checking for existing app template")
144-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
145-
return
146-
}
147-
148-
if existingAppTemplate.ID != uuid.Nil {
149-
appTemplate = existingAppTemplate
150-
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "update-app-template", Value: true})
151-
}
152-
if appTemplate == nil {
153-
appTemplate = &models.AppTemplate{
154-
ProjectID: int(project.ID),
155-
PorterAppID: int(porterApps[0].ID),
156-
}
157-
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "update-app-template", Value: false})
158-
}
159-
160118
protoWithoutDefaultAppEnvGroups, err := filterDefaultAppEnvGroups(ctx, request.B64AppProto, agent)
161119
if err != nil {
162120
err := telemetry.Error(ctx, span, err, "error filtering default app env groups")
163121
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
164122
return
165123
}
166124

167-
appTemplate.Base64App = protoWithoutDefaultAppEnvGroups
168-
appTemplate.BaseDeploymentTargetID = baseDeploymentTarget
169-
170-
updatedAppTemplate, err := c.Repo().AppTemplate().CreateAppTemplate(appTemplate)
171-
if err != nil {
172-
err := telemetry.Error(ctx, span, err, "error creating app template")
173-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
174-
return
175-
}
125+
var addonTemplates []*porterv1.AddonWithEnvVars
126+
for _, addon := range request.Addons {
127+
decoded, err := base64.StdEncoding.DecodeString(addon.Base64Addon)
128+
if err != nil {
129+
err := telemetry.Error(ctx, span, err, "error decoding base64 addon")
130+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
131+
return
132+
}
176133

177-
if updatedAppTemplate == nil {
178-
err := telemetry.Error(ctx, span, err, "updated app template is nil")
179-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
180-
return
181-
}
182-
if updatedAppTemplate.ID == uuid.Nil {
183-
err := telemetry.Error(ctx, span, err, "updated app template id is nil")
184-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
185-
return
186-
}
134+
addonProto := &porterv1.Addon{}
135+
err = helpers.UnmarshalContractObject(decoded, addonProto)
136+
if err != nil {
137+
err := telemetry.Error(ctx, span, err, "error unmarshalling addon proto")
138+
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
139+
return
140+
}
187141

188-
previewTemplateEnvName, err := porter_app.AppTemplateEnvGroupName(ctx, appName, cluster.ID, c.Repo().PorterApp())
189-
if err != nil {
190-
err := telemetry.Error(ctx, span, err, "unable to get app template env group name")
191-
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
192-
return
193-
}
142+
addonTemplates = append(addonTemplates, &porterv1.AddonWithEnvVars{
143+
Addon: addonProto,
144+
EnvVars: &porterv1.EnvGroupVariables{
145+
Normal: addon.Variables,
146+
Secret: addon.Secrets,
147+
},
148+
})
149+
}
150+
151+
updateAppTemplateReq := connect.NewRequest(&porterv1.UpdateAppTemplateRequest{
152+
ProjectId: int64(project.ID),
153+
AppName: appName,
154+
AppTemplate: protoWithoutDefaultAppEnvGroups,
155+
AppEnv: &porterv1.EnvGroupVariables{
156+
Normal: request.Variables,
157+
Secret: request.Secrets,
158+
},
159+
AddonTemplates: addonTemplates,
160+
BaseDeploymentTargetId: baseDeploymentTarget.String(),
161+
})
194162

195-
envGroup, err := environment_groups.LatestBaseEnvironmentGroup(ctx, agent, previewTemplateEnvName)
163+
updateAppTemplateRes, err := c.Config().ClusterControlPlaneClient.UpdateAppTemplate(ctx, updateAppTemplateReq)
196164
if err != nil {
197-
err := telemetry.Error(ctx, span, err, "unable to get latest base environment group")
165+
err := telemetry.Error(ctx, span, err, "error updating app template")
198166
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
199167
return
200168
}
201169

202-
if envGroup.Name == "" {
203-
envGroup = environment_groups.EnvironmentGroup{
204-
Name: previewTemplateEnvName,
205-
CreatedAtUTC: time.Now().UTC(),
206-
}
207-
}
208-
envGroup.Variables = request.Variables
209-
envGroup.SecretVariables = request.Secrets
210-
211-
additionalEnvGroupLabels := map[string]string{
212-
LabelKey_AppName: appName,
213-
environment_groups.LabelKey_DefaultAppEnvironment: "true",
214-
LabelKey_PorterManaged: "true",
215-
}
216-
217-
err = environment_groups.CreateOrUpdateBaseEnvironmentGroup(ctx, agent, envGroup, additionalEnvGroupLabels)
218-
if err != nil {
219-
err := telemetry.Error(ctx, span, err, "unable to create or update base environment group")
170+
if updateAppTemplateRes == nil || updateAppTemplateRes.Msg == nil {
171+
err := telemetry.Error(ctx, span, err, "error updating app template")
220172
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
221173
return
222174
}
@@ -238,45 +190,42 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
238190
return
239191
}
240192

241-
res := &CreateAppTemplateResponse{
242-
AppTemplateID: updatedAppTemplate.ID.String(),
243-
}
193+
res := &CreateAppTemplateResponse{}
244194

245195
c.WriteResult(w, r, res)
246196
}
247197

248198
// filterDefaultAppEnvGroups filters out any default app env groups found when creating an app template
249199
// app templates are based on the latest version of a given app, so it is possible for this env group to be included
250200
// however, the app template will get its own default env group when used to deploy to a preview environment
251-
func filterDefaultAppEnvGroups(ctx context.Context, b64AppProto string, agent *kubernetes.Agent) (string, error) {
201+
func filterDefaultAppEnvGroups(ctx context.Context, b64AppProto string, agent *kubernetes.Agent) (*porterv1.PorterApp, error) {
252202
ctx, span := telemetry.NewSpan(ctx, "filter-default-app-env-groups")
253203
defer span.End()
254204

255-
var finalAppProto string
205+
appProto := &porterv1.PorterApp{}
256206

257207
if b64AppProto == "" {
258-
return finalAppProto, telemetry.Error(ctx, span, nil, "b64 app proto is empty")
208+
return appProto, telemetry.Error(ctx, span, nil, "b64 app proto is empty")
259209
}
260210
if agent == nil {
261-
return finalAppProto, telemetry.Error(ctx, span, nil, "agent is nil")
211+
return appProto, telemetry.Error(ctx, span, nil, "agent is nil")
262212
}
263213

264214
decoded, err := base64.StdEncoding.DecodeString(b64AppProto)
265215
if err != nil {
266-
return finalAppProto, telemetry.Error(ctx, span, err, "error decoding base app")
216+
return appProto, telemetry.Error(ctx, span, err, "error decoding base app")
267217
}
268218

269-
appProto := &porterv1.PorterApp{}
270219
err = helpers.UnmarshalContractObject(decoded, appProto)
271220
if err != nil {
272-
return finalAppProto, telemetry.Error(ctx, span, err, "error unmarshalling app proto")
221+
return appProto, telemetry.Error(ctx, span, err, "error unmarshalling app proto")
273222
}
274223

275224
filteredEnvGroups := []*porterv1.EnvGroup{}
276225
for _, envGroup := range appProto.EnvGroups {
277226
baseEnvGroup, err := environment_groups.LatestBaseEnvironmentGroup(ctx, agent, envGroup.Name)
278227
if err != nil {
279-
return finalAppProto, telemetry.Error(ctx, span, err, "unable to get latest base environment group")
228+
return appProto, telemetry.Error(ctx, span, err, "unable to get latest base environment group")
280229
}
281230
if baseEnvGroup.DefaultAppEnvironment {
282231
continue
@@ -287,12 +236,5 @@ func filterDefaultAppEnvGroups(ctx context.Context, b64AppProto string, agent *k
287236

288237
appProto.EnvGroups = filteredEnvGroups
289238

290-
encoded, err := helpers.MarshalContractObject(ctx, appProto)
291-
if err != nil {
292-
return finalAppProto, telemetry.Error(ctx, span, err, "error marshalling app proto")
293-
}
294-
295-
finalAppProto = base64.StdEncoding.EncodeToString(encoded)
296-
297-
return finalAppProto, nil
239+
return appProto, nil
298240
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useMemo } from "react";
2+
import { PorterApp } from "@porter-dev/api-contracts";
3+
4+
import { type PopulatedEnvGroup } from "main/home/app-dashboard/validate-apply/app-settings/types";
5+
import {
6+
applyPreviewOverrides,
7+
clientAppFromProto,
8+
type ClientPorterApp,
9+
} from "lib/porter-apps";
10+
import { type DetectedServices } from "lib/porter-apps/services";
11+
12+
export const useAppWithPreviewOverrides = ({
13+
latestApp,
14+
detectedServices,
15+
templateEnv,
16+
existingTemplate,
17+
appEnv,
18+
}: {
19+
latestApp: PorterApp;
20+
detectedServices: DetectedServices | null;
21+
existingTemplate?: PorterApp;
22+
templateEnv?: {
23+
variables: Record<string, string>;
24+
secret_variables: Record<string, string>;
25+
};
26+
appEnv?: PopulatedEnvGroup;
27+
}): ClientPorterApp => {
28+
const withPreviewOverrides = useMemo(() => {
29+
const proto =
30+
existingTemplate ||
31+
new PorterApp({
32+
...latestApp,
33+
envGroups: [],
34+
}); // clear out env groups, they won't get added to the template anyways
35+
36+
const variables = templateEnv ? templateEnv.variables : appEnv?.variables;
37+
const secrets = templateEnv
38+
? templateEnv.secret_variables
39+
: appEnv?.secret_variables;
40+
41+
return applyPreviewOverrides({
42+
app: clientAppFromProto({
43+
proto,
44+
overrides: detectedServices,
45+
variables,
46+
secrets,
47+
lockServiceDeletions: true,
48+
}),
49+
overrides: detectedServices?.previews,
50+
});
51+
}, [
52+
latestApp,
53+
detectedServices,
54+
existingTemplate,
55+
templateEnv,
56+
appEnv?.variables,
57+
appEnv?.secret_variables,
58+
]);
59+
60+
return withPreviewOverrides;
61+
};

0 commit comments

Comments
 (0)