Repository navigation
Expand file tree
/
Copy pathUserDetails.java
More file actions
130 lines (113 loc) · 4.96 KB
/
Copy pathUserDetails.java
File metadata and controls
130 lines (113 loc) · 4.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package dev.getchat.sdk;
import java.time.Instant;
import java.util.Map;
import org.jspecify.annotations.Nullable;
/**
* A typed, read-only view over a user returned by the REST API — the
* {@code UserResource} shape in the backend's {@code openapi.yml}. This is what
* {@link GetChatClient#getUser(String)}, {@link GetChatClient#createUser} and
* {@link GetChatClient#updateUser} hand back.
*
* <p>Like {@link ChatDetails}, this is a <strong>lazy view</strong>, not an eager
* deserialisation: it wraps the underlying {@link JsonValue} and each accessor reads
* from it on call. Fields the backend adds in the future are never lost — reach them
* through {@link #raw()}, which returns the user's underlying {@code JsonValue}.
*
* <h2>Timestamps</h2>
* <p>Unlike {@link Message} (Unix epoch seconds), user timestamps are ISO-8601
* date-times, the same as {@link ChatDetails}; {@link #createdAt()} /
* {@link #updatedAt()} parse them to an {@link Instant}, and an absent or
* unparseable value yields {@code null}.
*
* <h2>Null policy</h2>
* <ul>
* <li>{@link #id()} and {@link #name()} are spec-required scalars and are returned
* non-null; if the backend violates the spec and omits one, a lenient empty
* string is returned rather than an exception.</li>
* <li>{@link #email()} and {@link #link()} are optional and return {@code null}
* when absent.</li>
* <li>Dates ({@link #createdAt()}, {@link #updatedAt()}) are {@code @Nullable
* Instant}; an absent or unparseable value yields {@code null}, never an
* exception.</li>
* <li>{@link #picture()} is a typed {@link Avatar} (a URL string <em>or</em> a
* generated-placeholder object), or {@code null} when absent.</li>
* <li>{@link #metadata()} is an unmodifiable {@code Map} of the user's scalar
* metadata; empty (never {@code null}) when absent.</li>
* </ul>
*
* <p>Two instances are equal when their underlying JSON is equal.
*/
public final class UserDetails {
private final JsonValue raw;
private UserDetails(JsonValue raw) {
this.raw = raw;
}
/** Wrap a user {@code JsonValue} (a {@code UserResource} object) as a typed view. */
public static UserDetails of(JsonValue raw) {
return new UserDetails(raw);
}
/** The underlying JSON, for fields without a typed accessor. */
public JsonValue raw() {
return raw;
}
/** External user id (spec-required; lenient empty string if the backend omits it). */
public String id() {
return raw.get("id").asString("");
}
/** User display name (spec-required; lenient empty string if the backend omits it). */
public String name() {
return raw.get("name").asString("");
}
/** User email, or {@code null} when unset. */
public @Nullable String email() {
JsonValue v = raw.get("email");
return v.isString() ? v.asString() : null;
}
/** User profile link, or {@code null} when unset. */
public @Nullable String link() {
JsonValue v = raw.get("link");
return v.isString() ? v.asString() : null;
}
/**
* The user's avatar as a typed {@link Avatar} — either a URL string (when an image
* is set) or a generated placeholder ({@code kind}/{@code color}/{@code initials}),
* distinguished with {@link Avatar#isUrl()}. {@code null} when the field is absent.
*/
public @Nullable Avatar picture() {
JsonValue p = raw.get("picture");
return (p.isString() || p.isObject()) ? Avatar.of(p) : null;
}
/** Creation time, or {@code null} when absent or unparseable. */
public @Nullable Instant createdAt() {
return ChatDetails.parseIso(raw.get("created_at"));
}
/** Last-update time, or {@code null} when absent or unparseable. */
public @Nullable Instant updatedAt() {
return ChatDetails.parseIso(raw.get("updated_at"));
}
/**
* The user's metadata as an unmodifiable {@code Map}, or an empty map (never
* {@code null}) when absent. Values are scalars — {@code String}, {@code Number}
* or {@code Boolean}; a JSON {@code null} or a nested object/array is dropped
* rather than surfaced. (The {@code UserResource} schema types these as strings,
* but the reader is lenient and keeps a numeric or boolean scalar as-is, matching
* chat metadata.) Reach the untyped form through {@link #raw()} if you need it.
*/
public Map<String, Object> metadata() {
return ChatDetails.scalarMap(raw.get("metadata"));
}
/** Equal when the underlying JSON is equal. */
@Override
public boolean equals(@Nullable Object o) {
return this == o || (o instanceof UserDetails other && raw.equals(other.raw));
}
@Override
public int hashCode() {
return raw.hashCode();
}
/** Compact summary for logs, e.g. {@code UserDetails{id=u-1, name=Alice}}. */
@Override
public String toString() {
return "UserDetails{id=" + id() + ", name=" + name() + "}";
}
}