Skip to content

Commit

Permalink
convert the workspace update api to patch style (#16739)
Browse files Browse the repository at this point in the history
* convert the workspace api to patch style

* check for empty email in workspace update
  • Loading branch information
mfsiega-airbyte authored Sep 26, 2022
1 parent b8a6472 commit 32a9841
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 24 deletions.
5 changes: 1 addition & 4 deletions airbyte-api/src/main/openapi/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2395,12 +2395,9 @@ components:
type: string
WorkspaceUpdate:
type: object
description: Used to apply a patch-style update to a workspace, which means that null properties remain unchanged
required:
- workspaceId
- initialSetupComplete
- anonymousDataCollection
- news
- securityUpdates
properties:
workspaceId:
$ref: "#/components/schemas/WorkspaceId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package io.airbyte.server.handlers;

import com.github.slugify.Slugify;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import io.airbyte.analytics.TrackingClientSingleton;
import io.airbyte.api.model.generated.ConnectionRead;
Expand Down Expand Up @@ -36,9 +37,12 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WorkspacesHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(WorkspacesHandler.class);
private final ConfigRepository configRepository;
private final ConnectionsHandler connectionsHandler;
private final DestinationHandler destinationHandler;
Expand Down Expand Up @@ -143,29 +147,28 @@ public WorkspaceRead getWorkspaceBySlug(final SlugRequestBody slugRequestBody)
return buildWorkspaceRead(workspace);
}

public WorkspaceRead updateWorkspace(final WorkspaceUpdate workspaceUpdate) throws ConfigNotFoundException, IOException, JsonValidationException {
final UUID workspaceId = workspaceUpdate.getWorkspaceId();
public WorkspaceRead updateWorkspace(final WorkspaceUpdate workspacePatch) throws ConfigNotFoundException, IOException, JsonValidationException {
final UUID workspaceId = workspacePatch.getWorkspaceId();

final StandardWorkspace persistedWorkspace = configRepository.getStandardWorkspace(workspaceId, false);
LOGGER.debug("Starting updateWorkspace for workspaceId {}...", workspaceId);
LOGGER.debug("Incoming workspacePatch: {}", workspacePatch);

if (!Strings.isNullOrEmpty(workspaceUpdate.getEmail())) {
persistedWorkspace.withEmail(workspaceUpdate.getEmail());
}
final StandardWorkspace workspace = configRepository.getStandardWorkspace(workspaceId, false);
LOGGER.debug("Initial workspace: {}", workspace);

persistedWorkspace
.withInitialSetupComplete(workspaceUpdate.getInitialSetupComplete())
.withDisplaySetupWizard(workspaceUpdate.getDisplaySetupWizard())
.withAnonymousDataCollection(workspaceUpdate.getAnonymousDataCollection())
.withNews(workspaceUpdate.getNews())
.withSecurityUpdates(workspaceUpdate.getSecurityUpdates())
.withNotifications(NotificationConverter.toConfigList(workspaceUpdate.getNotifications()));
validateWorkspacePatch(workspace, workspacePatch);

configRepository.writeStandardWorkspace(persistedWorkspace);
LOGGER.debug("Initial WorkspaceRead: {}", buildWorkspaceRead(workspace));

applyPatchToStandardWorkspace(workspace, workspacePatch);

LOGGER.debug("Patched Workspace before persisting: {}", workspace);
configRepository.writeStandardWorkspace(workspace);

// after updating email or tracking info, we need to re-identify the instance.
TrackingClientSingleton.get().identify(workspaceId);

return buildWorkspaceReadFromId(workspaceUpdate.getWorkspaceId());
return buildWorkspaceReadFromId(workspaceId);
}

public WorkspaceRead updateWorkspaceName(final WorkspaceUpdateName workspaceUpdateName)
Expand Down Expand Up @@ -249,4 +252,32 @@ private static WorkspaceRead buildWorkspaceRead(final StandardWorkspace workspac
.notifications(NotificationConverter.toApiList(workspace.getNotifications()));
}

private void validateWorkspacePatch(final StandardWorkspace persistedWorkspace, final WorkspaceUpdate workspacePatch) {
Preconditions.checkArgument(persistedWorkspace.getWorkspaceId().equals(workspacePatch.getWorkspaceId()));
}

private void applyPatchToStandardWorkspace(final StandardWorkspace workspace, final WorkspaceUpdate workspacePatch) {
if (workspacePatch.getAnonymousDataCollection() != null) {
workspace.setAnonymousDataCollection(workspacePatch.getAnonymousDataCollection());
}
if (workspacePatch.getNews() != null) {
workspace.setNews(workspacePatch.getNews());
}
if (workspacePatch.getDisplaySetupWizard() != null) {
workspace.setDisplaySetupWizard(workspacePatch.getDisplaySetupWizard());
}
if (workspacePatch.getSecurityUpdates() != null) {
workspace.setSecurityUpdates(workspacePatch.getSecurityUpdates());
}
if (!Strings.isNullOrEmpty(workspacePatch.getEmail())) {
workspace.setEmail(workspacePatch.getEmail());
}
if (workspacePatch.getInitialSetupComplete() != null) {
workspace.setInitialSetupComplete(workspacePatch.getInitialSetupComplete());
}
if (workspacePatch.getNotifications() != null) {
workspace.setNotifications(NotificationConverter.toConfigList(workspacePatch.getNotifications()));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,38 @@ void testUpdateWorkspaceNoNameUpdate() throws JsonValidationException, ConfigNot
assertEquals(expectedWorkspaceRead, actualWorkspaceRead);
}

@Test
@DisplayName("Partial patch update should preserve unchanged fields")
void testWorkspacePatchUpdate() throws JsonValidationException, ConfigNotFoundException, IOException {
final String EXPECTED_NEW_EMAIL = "expected-new-email@example.com";
final WorkspaceUpdate workspaceUpdate = new WorkspaceUpdate()
.workspaceId(workspace.getWorkspaceId())
.anonymousDataCollection(true)
.email(EXPECTED_NEW_EMAIL);

final StandardWorkspace expectedWorkspace = Jsons.clone(workspace).withEmail(EXPECTED_NEW_EMAIL).withAnonymousDataCollection(true);
when(configRepository.getStandardWorkspace(workspace.getWorkspaceId(), false))
.thenReturn(workspace)
.thenReturn(expectedWorkspace);
// The same as the original workspace, with only the email and data collection flags changed.
final WorkspaceRead expectedWorkspaceRead = new WorkspaceRead()
.workspaceId(workspace.getWorkspaceId())
.customerId(workspace.getCustomerId())
.email(EXPECTED_NEW_EMAIL)
.name(workspace.getName())
.slug(workspace.getSlug())
.initialSetupComplete(workspace.getInitialSetupComplete())
.displaySetupWizard(workspace.getDisplaySetupWizard())
.news(workspace.getNews())
.anonymousDataCollection(true)
.securityUpdates(workspace.getSecurityUpdates())
.notifications(NotificationConverter.toApiList(workspace.getNotifications()));

final WorkspaceRead actualWorkspaceRead = workspacesHandler.updateWorkspace(workspaceUpdate);
verify(configRepository).writeStandardWorkspace(expectedWorkspace);
assertEquals(expectedWorkspaceRead, actualWorkspaceRead);
}

@Test
void testSetFeedbackDone() throws JsonValidationException, ConfigNotFoundException, IOException {
final WorkspaceGiveFeedback workspaceGiveFeedback = new WorkspaceGiveFeedback()
Expand Down
10 changes: 5 additions & 5 deletions docs/reference/api/generated-api-html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11928,15 +11928,15 @@ <h3><a name="WorkspaceReadList"><code>WorkspaceReadList</code> - </a> <a class="
</div>
<div class="model">
<h3><a name="WorkspaceUpdate"><code>WorkspaceUpdate</code> - </a> <a class="up" href="#__Models">Up</a></h3>
<div class='model-description'></div>
<div class='model-description'>Used to apply a patch-style update to a workspace, which means that null properties remain unchanged</div>
<div class="field-items">
<div class="param">workspaceId </div><div class="param-desc"><span class="param-type"><a href="#UUID">UUID</a></span> format: uuid</div>
<div class="param">email (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> format: email</div>
<div class="param">initialSetupComplete </div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">initialSetupComplete (optional)</div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">displaySetupWizard (optional)</div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">anonymousDataCollection </div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">news </div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">securityUpdates </div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">anonymousDataCollection (optional)</div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">news (optional)</div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">securityUpdates (optional)</div><div class="param-desc"><span class="param-type"><a href="#boolean">Boolean</a></span> </div>
<div class="param">notifications (optional)</div><div class="param-desc"><span class="param-type"><a href="#Notification">array[Notification]</a></span> </div>
</div> <!-- field-items -->
</div>
Expand Down

0 comments on commit 32a9841

Please sign in to comment.