Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare RepositoryMetadata role Updates for #1060 Compatibility #1061

Merged
merged 18 commits into from
Dec 16, 2024

Conversation

minwoox
Copy link
Member

@minwoox minwoox commented Nov 14, 2024

Motivation
To ensure smooth compatibility with #1060, the RepositoryMetadata structure needs an update to support changes.
AS-IS:

{
  "name": "minu-test",
  "perRolePermissions": {
    "owner": [
      "READ",
      "WRITE"
    ],
    "member": ["READ"],
    "guest": []
  },
  "perUserPermissions": {
    "foo@dogma.com": [
      "READ"
    ],
    "bar@dogma.com": [
      "READ",
      "WRITE"
    ]
  },
  "perTokenPermissions": {
    "goodman": [
      "READ"
    ]
  },
  "creation": {
    "user": "minu.song@dogma.com",
    "timestamp": "2024-08-19T02:47:23.370762417Z"
  }
}

TO-BE:

{
  "name": "minu-test",
  "roles": {
    "projects": {
      "member": "READ",
      "guest": null
    }
    "users": {
      "foo@dogma.com": "READ",
      "bar@dogma.com": "WRITE"
    },
    "tokens": {
      "goodman": "READ"
    }
  },
  "creation": {
    "user": "minu.song@dogma.com",
    "timestamp": "2024-08-19T02:47:23.370762417Z"
  }
}

Modifications:

  • Enhanced the RepositoryMetadata deserializers to accept both legacy and new formats.
  • Added RepositoryRole.
    • It will be used for repository instead of Permission.
  • Removed MetadataApiService.updateSpecificUserPermission and updateSpecificTokenPermission.
    • They are not used in the UI and we don't even have test cases for that.
    • Will add those APIs later with better API design when we need it.

Result:

Motivation
To ensure smooth compatibility with line#1060, the `PerRolePermissions` and `RepositoryMetadata` structure need an update to support changes.

Modifications:
- Enhanced the `PerRolePermissions` and `RepositoryMetadata` deserializers to accept both an array or permissions and a permission.
- Added `REPO_ADMIN` `Permission`.
- Removed `MetadataApiService.updateSpecificUserPermission` and `updateSpecificTokenPermission`.
  - They are not used in the UI and we don't even have test cases for that.
  - Will add those APIs later when we need it.

Result:
- The `PerRolePermissions` and `RepositoryMetadata` structures now support the upcoming changes in line#1060.
@minwoox minwoox added this to the 0.71.0 milestone Nov 14, 2024
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍

import com.linecorp.centraldogma.internal.Jackson;
import com.linecorp.centraldogma.server.QuotaConfig;

final class RepositoryMetadataDeserializer extends StdDeserializer<RepositoryMetadata> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood these custom deserializers will be removed once migration is complete

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct. 👍 👍

@minwoox minwoox changed the title Prepare PerPermission Updates for #1060 Compatibility Prepare RepositoryMetadata role Updates for #1060 Compatibility Nov 19, 2024
@minwoox minwoox changed the title Prepare RepositoryMetadata role Updates for #1060 Compatibility [WIP] Prepare RepositoryMetadata role Updates for #1060 Compatibility Nov 19, 2024
Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍

@minwoox minwoox changed the title [WIP] Prepare RepositoryMetadata role Updates for #1060 Compatibility Prepare RepositoryMetadata role Updates for #1060 Compatibility Nov 22, 2024
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood the new changes

* Returns the {@link ProjectRoles} of the repository.
*/
@JsonProperty("projects")
public ProjectRoles projectRoles() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood this property as: the default RepositoryRole that will be assigned to project members that aren't explicitly set by users and tokens.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the default RepositoryRole that will be assigned to project members

That's correct. 👍

that aren't explicitly set by users and tokens.

That is not correct. A RepositoryRole can still be assigned to a user. The RepositoryRole, which operates at a higher level, will be applied in such situation. https://github.com/line/centraldogma/pull/1060/files#diff-dc8a031ec8b91f16d76b42e152e9c3be8dd07b6f77bea922030074acc78ce78eR800-R823

throws IOException {
final JsonNode jsonNode = p.readValueAsTree();
final String name = jsonNode.get("name").textValue();
final JsonNode perRolePermissionsNode = jsonNode.get("perRolePermissions");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I understood that if both perRolePermissions and roles is in the json node, then perRolePermissions (the legacy format) has higher priority.

Copy link
Member Author

@minwoox minwoox Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood that if both perRolePermissions and roles is in the json node

They cannot be. Let me add an assertion logic. 😉

*/
WRITE,
/**
* Able to manage a repository.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compared with the current permission scheme, I understood the difference is:

  1. the ability to set ADMIN for a repository
  2. repository settings can't change project admin roles.

@minwoox minwoox modified the milestones: 0.71.0, 0.72.0 Dec 4, 2024
@minwoox minwoox marked this pull request as draft December 5, 2024 01:26
@minwoox
Copy link
Member Author

minwoox commented Dec 5, 2024

This PR will be ready after #1069 is merged.

.thenApply(CommitResult::revision)
.exceptionally(cause -> {
final Throwable peeled = Exceptions.peel(cause);
if (peeled instanceof RedundantChangeException) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've realized that we don't need this logic anymore after introducing the transformer in #1069 because it will raise an IllegalArgumentException if there's no change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure IllegalArgumentException is a proper exception to understand the context.

@minwoox minwoox marked this pull request as ready for review December 11, 2024 03:26
final RepositoryMetadata repositoryMetadata = projectMetadata.repo(repoName);
assert repositoryMetadata.name().equals(repoName);
if (repositoryMetadata.perRolePermissions().equals(perRolePermissions)) {
throw new IllegalArgumentException(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of raising RedundantChangeException instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion! How about the following approach:

  • Introduce UserNotFoundException
    • This exception would be used exclusively on the server side, similar to TokenNotFoundException.
  • Raise UserNotFoundException and TokenNotFoundException
    • These exceptions would be triggered when attempting to update an entity that doesn't exist.
  • Raise RedundantChangeException
    • This exception would be raised when an update is attempted with the same content as the existing one.
    • The RedundantChangeException would be swallowed, and the system would simply return the head revision. In other words, users wouldn’t encounter this exception."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, PTAL. 🙇

.thenApply(CommitResult::revision)
.exceptionally(cause -> {
final Throwable peeled = Exceptions.peel(cause);
if (peeled instanceof RedundantChangeException) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure IllegalArgumentException is a proper exception to understand the context.

final String commitSummary =
"Update the role permission of the '" + repoName + "' in the project '" + projectName + '\'';
return metadataRepo.push(projectName, Project.REPO_DOGMA, author, commitSummary, change);
final ContentTransformer<JsonNode> transformer = new ContentTransformer<>(
METADATA_JSON, EntryType.JSON, node -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional) I could see some code similar to this in other places. How about creating RepositoryMetadataTransformer and duplicating them?

private static class RepositoryMetadataTransformer extends ContentTransformer {
    
   RepositoryMetadataTransformer(Function<RepositoryMetadata, RepositoryMetadata>) {
       ...
   }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion. 👍
RepositoryMetadataTransformer, ProjectMetadataTransformer and TokensTransformer are added. 😉

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! 👍

Comment on lines +1092 to +1098
for (Entry<String, Token> entry : tokens.appIds().entrySet()) {
if (!entry.getKey().equals(appId)) {
appIdsBuilder.put(entry);
} else {
appIdsBuilder.put(appId, newToken);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: It seems useful if we have an ImmutableMap utility that helps to update or remove.

class ImmutableMapUtil {
    Map<K, V> remove(Map<K, V> old, K key) { 
      // create a new map and remove `key`.
      ... 
    }

    Map<K, V> add(Map<K, V> old, K key, V value) { ... }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks useful. 👍 Let me try that later. 😉

@minwoox minwoox merged commit 1cafcd1 into line:main Dec 16, 2024
9 of 10 checks passed
@minwoox minwoox deleted the prework_1060 branch December 16, 2024 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants