public PropertiesSnapshot updateWorkflowProperties(
      String workflowId, User author, Properties props, PropertiesUpdate update) {
    LOG.debug("Updating workflow properties for workflow id [{}]", workflowId);
    Checks.notNull(
        props, "properties changes to apply cannot be null for workflow [%s]", workflowId);
    return withMetricLogError(
        () ->
            withRetryableTransaction(
                conn -> {
                  WorkflowInfo workflowInfo = getWorkflowInfoForUpdate(conn, workflowId);
                  Checks.notNull(
                      workflowInfo.getPrevPropertiesSnapshot(),
                      "Cannot update workflow properties while the workflow [%s] does not exist",
                      workflowId);
                  PropertiesSnapshot snapshot =
                      updateWorkflowProps(
                          conn,
                          workflowId,
                          author,
                          System.currentTimeMillis(),
                          workflowInfo.getPrevPropertiesSnapshot(),
                          props,
                          update);

                  List<StatementPreparer> preparers = new ArrayList<>();
                  StringBuilder fields = prepareProperties(preparers, workflowId, snapshot);

                  long[] updateRes = executeTemplateUpdate(conn, fields, preparers);

                  if (updateRes != null) {
                    if (workflowInfo.getPrevActiveVersionId() != Constants.INACTIVE_VERSION_ID) {
                      updateWorkflowInfoForNextActiveWorkflow(
                          conn,
                          workflowId,
                          workflowInfo.getPrevActiveVersionId(),
                          workflowInfo,
                          snapshot);
                      addWorkflowTriggersIfNeeded(conn, workflowInfo);
                    }

                    MaestroJobEvent jobEvent = logToTimeline(conn, workflowId, snapshot);
                    publisher.publishOrThrow(
                        jobEvent, "Failed to publish maestro properties change job event.");
                  }
                  return snapshot;
                }),
        "updateWorkflowProperties",
        "Failed updating the properties for workflow [{}]",
        workflowId);
  }
@Test
  public void testUpdateWorkflowProperties() throws Exception {
    WorkflowDefinition wfd = loadWorkflow(TEST_WORKFLOW_ID2);
    workflowDao.addWorkflowDefinition(wfd, wfd.getPropertiesSnapshot().extractProperties());
    assertNotNull(wfd.getInternalId());
    verify(publisher, times(1)).publishOrThrow(any(), any());
    MaestroWorkflow maestroWorkflow = workflowDao.getMaestroWorkflow(TEST_WORKFLOW_ID2);
    assertEquals("tester", maestroWorkflow.getPropertiesSnapshot().getOwner().getName());
    assertEquals(1L, maestroWorkflow.getLatestVersionId().longValue());
    PropertiesSnapshot newSnapshot =
        workflowDao.updateWorkflowProperties(
            TEST_WORKFLOW_ID2, User.create("test"), new Properties(), PROPERTIES_UPDATE);
    assertEquals(
        wfd.getPropertiesSnapshot().toBuilder()
            .createTime(newSnapshot.getCreateTime())
            .author(User.create("test"))
            .build(),
        newSnapshot);
    verify(publisher, times(2)).publishOrThrow(any(), any());
    maestroWorkflow = workflowDao.getMaestroWorkflow(TEST_WORKFLOW_ID2);
    assertEquals("tester", maestroWorkflow.getPropertiesSnapshot().getOwner().getName());
    Properties props = new Properties();
    props.setOwner(User.create("another-owner"));
    workflowDao.updateWorkflowProperties(
        TEST_WORKFLOW_ID2, User.create("test"), props, PROPERTIES_UPDATE);
    verify(publisher, times(3)).publishOrThrow(any(), any());
    maestroWorkflow = workflowDao.getMaestroWorkflow(TEST_WORKFLOW_ID2);
    assertEquals("another-owner", maestroWorkflow.getPropertiesSnapshot().getOwner().getName());
    assertEquals(wfd, workflowDao.addWorkflowDefinition(wfd, null));
    verify(triggerClient, times(0)).upsertTriggerSubscription(any(), any(), any());
  }