-
Notifications
You must be signed in to change notification settings - Fork 3
feat: enhance identifier handling with new generated model #64
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
base: main
Are you sure you want to change the base?
Changes from all commits
106a83e
446f364
3810e9b
7e9a244
babb9ea
678cc85
5e5f848
0ab4dd0
4cca638
0126fbc
2b4451d
c099e48
68dd696
1f5c65f
91ee571
45f4e58
305e1ae
cbf3016
d4243dd
50ed3aa
e604625
b00ea03
3970072
bcffb6a
fc304fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,6 @@ | |
| import org.cardanofoundation.signify.cesr.util.CoreUtil.Serials; | ||
| import org.cardanofoundation.signify.core.Eventing; | ||
| import org.cardanofoundation.signify.core.Httping; | ||
| import org.cardanofoundation.signify.core.States; | ||
| import org.cardanofoundation.signify.core.Manager.Algos; | ||
|
|
||
| import java.io.IOException; | ||
|
|
@@ -27,19 +26,22 @@ | |
| import java.security.DigestException; | ||
| import java.util.*; | ||
| import java.util.concurrent.ExecutionException; | ||
| import org.cardanofoundation.signify.generated.keria.model.EndrolesAidPostRequest; | ||
| import org.cardanofoundation.signify.generated.keria.model.Identifier; | ||
| import org.cardanofoundation.signify.generated.keria.model.KeyStateRecord; | ||
|
|
||
| import static org.cardanofoundation.signify.cesr.util.CoreUtil.Versionage; | ||
| import static org.cardanofoundation.signify.core.Httping.parseRangeHeaders; | ||
|
|
||
| public class Identifier { | ||
| public class IdentifierController { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering why we need to change this class name?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class name is defined in Signify itself (not a type from KERIA), so I guess it was renamed to not conflict with the generated However, it's not really a HTTP controller. This is the class used in |
||
| public final IdentifierDeps client; | ||
|
|
||
| /** | ||
| * Identifier | ||
| * | ||
| * @param client the client dependencies | ||
| */ | ||
| public Identifier(IdentifierDeps client) { | ||
| public IdentifierController(IdentifierDeps client) { | ||
| this.client = client; | ||
| } | ||
|
|
||
|
|
@@ -68,7 +70,7 @@ public IdentifierListResponse list(Integer start, Integer end) throws Interrupte | |
| range.start(), | ||
| range.end(), | ||
| range.total(), | ||
| response.body() | ||
| Arrays.asList(Utils.fromJson(response.body(), Identifier[].class)) | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -86,7 +88,7 @@ public IdentifierListResponse list(Integer start) throws IOException, Interrupte | |
| * @param name Prefix or alias of the identifier | ||
| * @return An Optional containing the HabState if found, or empty if not found | ||
| */ | ||
| public Optional<States.HabState> get(String name) throws InterruptedException, IOException, LibsodiumException { | ||
| public Optional<Identifier> get(String name) throws InterruptedException, IOException, LibsodiumException { | ||
| final String path = "/identifiers/" + URI.create(name).toASCIIString(); | ||
| final String method = "GET"; | ||
|
|
||
|
|
@@ -96,7 +98,7 @@ public Optional<States.HabState> get(String name) throws InterruptedException, I | |
| return Optional.empty(); | ||
| } | ||
|
|
||
| return Optional.of(Utils.fromJson(response.body(), States.HabState.class)); | ||
| return Optional.of(Utils.fromJson(response.body(), Identifier.class)); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -106,12 +108,16 @@ public Optional<States.HabState> get(String name) throws InterruptedException, I | |
| * @param info Information to update for the given identifier | ||
| * @return A HabState to the identifier information after updating | ||
| */ | ||
| public States.HabState update(String name, IdentifierInfo info) throws InterruptedException, IOException, LibsodiumException { | ||
| public Identifier update(String name, IdentifierInfo info) throws InterruptedException, IOException, LibsodiumException { | ||
| final String path = "/identifiers/" + name; | ||
| final String method = "PUT"; | ||
|
|
||
| HttpResponse<String> response = this.client.fetch(path, method, info); | ||
| return Utils.fromJson(response.body(), States.HabState.class); | ||
| HttpResponse<String> response = this.client.fetch( | ||
| path, | ||
| method, | ||
| info | ||
| ); | ||
| return Utils.fromJson(response.body(), Identifier.class); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -244,15 +250,16 @@ public EventResult create(String name, CreateIdentifierArgs kargs) throws Interr | |
| List<String> rmids = null; | ||
|
|
||
| if (states != null) { | ||
| List<States.State> stateDeserialized = Utils.fromJson(Utils.jsonStringify(states), new TypeReference<>() {}); | ||
| smids = stateDeserialized.stream().map(States.State::getI).toList(); | ||
| List<KeyStateRecord> stateDeserialized = Utils.fromJson(Utils.jsonStringify(states), new TypeReference<>() {}); | ||
| smids = stateDeserialized.stream().map(KeyStateRecord::getI).toList(); | ||
| } | ||
|
|
||
| if (rstates != null) { | ||
| List<States.State> rstateDeserialized = Utils.fromJson(Utils.jsonStringify(rstates), new TypeReference<>() {}); | ||
| rmids = rstateDeserialized.stream().map(States.State::getI).toList(); | ||
| List<KeyStateRecord> rstateDeserialized = Utils.fromJson(Utils.jsonStringify(rstates), new TypeReference<>() {}); | ||
| rmids = rstateDeserialized.stream().map(KeyStateRecord::getI).toList(); | ||
| } | ||
|
|
||
| // TODO use generated model request IdentifiersPostRequest, when it supports dynamic fields (proxy, smids, rmids) | ||
| Map<String, Object> jsondata = new LinkedHashMap<>(); | ||
| jsondata.put("name", name); | ||
| jsondata.put("icp", serder.getKed()); | ||
|
|
@@ -282,7 +289,7 @@ public EventResult create(String name, CreateIdentifierArgs kargs) throws Interr | |
| * @throws LibsodiumException if there is an error in the cryptographic operations | ||
| */ | ||
| public EventResult addEndRole(String name, String role, String eid, String stamp) throws InterruptedException, DigestException, IOException, LibsodiumException { | ||
| States.HabState hab = this.get(name) | ||
| Identifier hab = this.get(name) | ||
| .orElseThrow(() -> new IllegalArgumentException("Identifier not found: " + name)); | ||
| String pre = hab.getPrefix(); | ||
|
|
||
|
|
@@ -292,14 +299,14 @@ public EventResult addEndRole(String name, String role, String eid, String stamp | |
| Keeping.SignResult signResult = keeper.sign(rpy.getRaw().getBytes()); | ||
| List<String> sigs = signResult.signatures(); | ||
|
|
||
| LinkedHashMap<String, Object> jsondata = new LinkedHashMap<>(); | ||
| jsondata.put("rpy", rpy.getKed()); | ||
| jsondata.put("sigs", sigs); | ||
| EndrolesAidPostRequest endrolesAidPostRequest = new EndrolesAidPostRequest() | ||
| .rpy(rpy.getKed()) | ||
| .sigs(sigs); | ||
|
|
||
| HttpResponse<String> res = this.client.fetch( | ||
| "/identifiers/" + name + "/endroles", | ||
| "POST", | ||
| jsondata | ||
| endrolesAidPostRequest | ||
| ); | ||
| return new EventResult(rpy, sigs, res); | ||
| } | ||
|
|
@@ -337,11 +344,11 @@ public EventResult interact(String name, Object data) throws InterruptedExceptio | |
| } | ||
|
|
||
| public InteractionResponse createInteract(String name, Object data) throws InterruptedException, DigestException, IOException, LibsodiumException { | ||
| States.HabState hab = this.get(name) | ||
| Identifier hab = this.get(name) | ||
| .orElseThrow(() -> new IllegalArgumentException("Identifier not found: " + name)); | ||
| String pre = hab.getPrefix(); | ||
|
|
||
| States.State state = hab.getState(); | ||
| KeyStateRecord state = hab.getState(); | ||
| int sn = Integer.parseInt(state.getS(), 16); | ||
| String dig = state.getD(); | ||
|
|
||
|
|
@@ -376,12 +383,12 @@ public EventResult rotate(String name, RotateIdentifierArgs kargs) throws Interr | |
| String ncode = kargs.getNcode() != null ? kargs.getNcode() : MatterCodex.Ed25519_Seed.getValue(); | ||
| int ncount = kargs.getNcount() != null ? kargs.getNcount() : 1; | ||
|
|
||
| States.HabState hab = this.get(name) | ||
| Identifier hab = this.get(name) | ||
| .orElseThrow(() -> new IllegalArgumentException("Identifier not found: " + name)); | ||
| String pre = hab.getPrefix(); | ||
| boolean delegated = !hab.getState().getDi().isEmpty(); | ||
|
|
||
| States.State state = hab.getState(); | ||
| KeyStateRecord state = hab.getState(); | ||
| int count = state.getK().size(); | ||
| String dig = state.getD(); | ||
| int ridx = Integer.parseInt(state.getS(), 16) + 1; | ||
|
|
@@ -407,8 +414,8 @@ public EventResult rotate(String name, RotateIdentifierArgs kargs) throws Interr | |
| // Create new keys for next digests | ||
| List<String> ncodes = kargs.getNcodes() != null ? kargs.getNcodes() : Collections.nCopies(ncount, ncode); | ||
|
|
||
| List<States.State> states = kargs.getStates() == null ? new ArrayList<>() : kargs.getStates(); | ||
| List<States.State> rstates = kargs.getStates() == null ? new ArrayList<>() : kargs.getRstates(); | ||
| List<KeyStateRecord> states = kargs.getStates() == null ? new ArrayList<>() : kargs.getStates(); | ||
| List<KeyStateRecord> rstates = kargs.getStates() == null ? new ArrayList<>() : kargs.getRstates(); | ||
| KeeperResult keeperResult = keeper.rotate( | ||
| ncodes, | ||
| transferable, | ||
|
|
@@ -446,8 +453,8 @@ public EventResult rotate(String name, RotateIdentifierArgs kargs) throws Interr | |
| Map<String, Object> jsondata = new LinkedHashMap<>(); | ||
| jsondata.put("rot", serder.getKed()); | ||
| jsondata.put("sigs", sigs); | ||
| jsondata.put("smids", !states.isEmpty() ? states.stream().map(States.State::getI).toList() : null); | ||
| jsondata.put("rmids", !rstates.isEmpty() ? rstates.stream().map(States.State::getI).toList() : null); | ||
| jsondata.put("smids", !states.isEmpty() ? states.stream().map(KeyStateRecord::getI).toList() : null); | ||
| jsondata.put("rmids", !rstates.isEmpty() ? rstates.stream().map(KeyStateRecord::getI).toList() : null); | ||
| jsondata.put(keeper.getAlgo().toString(), keeper.getParams().toMap()); | ||
|
|
||
| HttpResponse<String> res = this.client.fetch( | ||
|
|
@@ -472,4 +479,4 @@ public Object members(String name) throws LibsodiumException, InterruptedExcepti | |
| ); | ||
| return Utils.fromJson(response.body(), Object.class); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,8 @@ | ||
| package org.cardanofoundation.signify.app.aiding; | ||
|
|
||
| public record IdentifierListResponse(int start, int end, int total, Object aids) { | ||
| import org.cardanofoundation.signify.generated.keria.model.Identifier; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record IdentifierListResponse(int start, int end, int total, List<Identifier> aids) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package org.cardanofoundation.signify.app.aiding; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonParser; | ||
| import com.fasterxml.jackson.databind.DeserializationContext; | ||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.deser.std.StdDeserializer; | ||
| import com.fasterxml.jackson.databind.node.ObjectNode; | ||
| import org.cardanofoundation.signify.generated.keria.model.KeyStateRecord; | ||
| import org.cardanofoundation.signify.app.config.GeneratedModelConfig; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| /** | ||
| * Allows kt/nt fields to arrive as arrays/objects and coerces them to strings for the generated model. | ||
| */ | ||
| public class KeyStateRecordDeserializer extends StdDeserializer<KeyStateRecord> { | ||
|
|
||
| private static final ObjectMapper delegate = GeneratedModelConfig.baseMapper(); | ||
|
|
||
| public KeyStateRecordDeserializer() { | ||
| super(KeyStateRecord.class); | ||
| } | ||
|
|
||
| @Override | ||
| public KeyStateRecord deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { | ||
| ObjectMapper mapper = (ObjectMapper) p.getCodec(); | ||
| ObjectNode node = mapper.readTree(p); | ||
|
|
||
| coerceToString(node, "kt"); | ||
| coerceToString(node, "nt"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really ever get ints back from the API? I thought it was always strings. (I know the API for creating an identifier accepts both though, which is different)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We hit a parse failure when
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, but it's an array of strings. I think @Sotatek-Patrick-Vu might be fixing this, or have fixed it. We should return the string array, not a string.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated in
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right but is it possible to receive a numeric response from the API? I don't think it is (may be a mistake in Patrick's generated models)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Sotatek-Patrick-Vu could you please take a look for these field ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are expecting
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Sotatek-DucPhung @Sotatek-PhongVu @Sotatek-Patrick-Vu It's also not generated correctly in Signify-TS: https://github.com/WebOfTrust/signify-ts/blob/main/src/types/keria-api-schema.ts#L363 This is referring to the Key State only. So I think there are missing changes in KERIA to generate this correctly in both Signify-TS and Signify-Java: cardano-foundation/keria#16
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After fixing keria and here is the new type in signify-ts:
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed @Sotatek-Patrick-Vu if you can fix this PR to remove |
||
|
|
||
| return delegate.treeToValue(node, KeyStateRecord.class); | ||
| } | ||
|
|
||
| private void coerceToString(ObjectNode node, String field) { | ||
| JsonNode value = node.get(field); | ||
| if (value == null) { | ||
| return; | ||
| } | ||
| // If already a string, leave it | ||
| if (value.isTextual()) { | ||
| return; | ||
| } | ||
| // If it's an array, convert to JSON string representation | ||
| if (value.isArray()) { | ||
| node.put(field, value.toString()); | ||
| return; | ||
| } | ||
| // If it's a number, convert to string | ||
| if (value.isNumber()) { | ||
| node.put(field, value.asText()); | ||
| return; | ||
| } | ||
| throw new IllegalArgumentException("Unexpected type for field '" + field + "': " + value.getNodeType()); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pointer to https://github.com/cardano-foundation/cf-signify-java/pull/59/files#r2604004041
Should be resolved before this PR is merged!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sotatek-DucPhung Please check my comment there: #59 (comment)
Those changes MUST be fixed in this PR before merging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it will take time to fix those, and we resolve the other comment in this thread first. We could defer fixing these until the next PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, sounds good to me