Skip to content

Commit

Permalink
Replace handle with did for mention facet
Browse files Browse the repository at this point in the history
  • Loading branch information
jdpgrailsdev committed Dec 9, 2024
1 parent e3efbc2 commit 4db6392
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 6 deletions.
1 change: 1 addition & 0 deletions autobot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ tasks.register<Test>("intTest") {
"TWITTER_OAUTH2_CLIENT_SECRET" to "secret",
"TWITTER_OAUTH2_ACCESS_TOKEN" to "",
"TWITTER_OAUTH2_REFRESH_TOKEN" to "",
"BLUESKY_PUBLIC_URL" to "http://localhost:9093",
"BLUESKY_URL" to "http://localhost:9093",
"BLUESKY_HANDLE" to "user",
"BLUESKY_PASSWORD" to "password",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import static com.jdpgrailsdev.oasis.timeline.WireMockInitializer.TEST_PORT;
import static com.jdpgrailsdev.oasis.timeline.client.BlueSkyClientKt.BLUE_SKY_CREATE_RECORD_URI;
import static com.jdpgrailsdev.oasis.timeline.client.BlueSkyClientKt.BLUE_SKY_CREATE_SESSION_URI;
import static com.jdpgrailsdev.oasis.timeline.client.BlueSkyClientKt.BLUE_SKY_GET_PROFILE_URI;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand Down Expand Up @@ -104,6 +105,8 @@ class EndToEndIntegrationTests {
"/bluesky_create_record_response.json";
private static final String BLUESKY_CREATE_SESSION_RESPONSE_FILE =
"/bluesky_create_session_response.json";
private static final String BLUESKY_GET_PROFILE_RESPONSE_FILE =
"/bluesky_get_profile_response.json";
private static final String RECORD_BODY_MESSAGE = "expected record message body";
private static final String SIZE_ASSERTION_MESSAGE = "expected number of tweets produced";
private static final String TWEET_BODY_MESSAGE = "expected tweet message body";
Expand Down Expand Up @@ -625,6 +628,10 @@ private void createPostStubs() throws IOException {
new String(
Files.readAllBytes(
new ClassPathResource(BLUESKY_CREATE_SESSION_RESPONSE_FILE).getFile().toPath()));
final String blueSkyProfileResponse =
new String(
Files.readAllBytes(
new ClassPathResource(BLUESKY_GET_PROFILE_RESPONSE_FILE).getFile().toPath()));
final String twitterResponse =
new String(
Files.readAllBytes(new ClassPathResource(TWITTER_RESPONSE_FILE).getFile().toPath()));
Expand All @@ -633,6 +640,10 @@ private void createPostStubs() throws IOException {
stubFor(
post(urlEqualTo(BLUE_SKY_CREATE_SESSION_URI)).willReturn(okJson(blueSkySessionResponse)));
stubFor(post(urlEqualTo(BLUE_SKY_CREATE_RECORD_URI)).willReturn(okJson(blueSkyResponse)));
stubFor(
com.github.tomakehurst.wiremock.client.WireMock.get(
urlEqualTo(BLUE_SKY_GET_PROFILE_URI + "?actor=noelgallagherlive.bsky.social"))
.willReturn(okJson(blueSkyProfileResponse)));
}

private List<ServeEvent> getServeEvents(final PostTarget postTarget) {
Expand Down
23 changes: 23 additions & 0 deletions autobot/src/intTest/resources/bluesky_get_profile_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"did": "did:plc:2kfn5kwq4dqzncgv2g2tqmii",
"handle": "noelgallagherlive.bsky.social",
"displayName": "Noel Gallagher",
"avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:2kfn5kwq4dqzncgv2g2tqmii/bafkreibt6uj54ks5rdtfhgbmf45vtc57klsvxgh3edc5273pfljczihz7u@jpeg",
"associated": {
"lists": 0,
"feedgens": 0,
"starterPacks": 0,
"labeler": false,
"chat": {
"allowIncoming": "all"
}
},
"labels": [],
"createdAt": "2024-05-24T13:18:25.063Z",
"description": "Official Noel Gallagher Bluesky page maintained by his label Sour Mash. New album 'Council Skies' - Out now.\n\nnoelgallagher.com",
"indexedAt": "2024-11-29T12:28:08.346Z",
"banner": "https://cdn.bsky.app/img/banner/plain/did:plc:2kfn5kwq4dqzncgv2g2tqmii/bafkreifuytbptl4tyxdxplpxgllouiqhupp5fhlcdbd7546j6ihy3pck4a@jpeg",
"followersCount": 2342,
"followsCount": 1,
"postsCount": 17
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package com.jdpgrailsdev.oasis.timeline.client

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
Expand All @@ -35,11 +36,13 @@ import java.io.IOException
const val BLUE_SKY_CREATE_SESSION_URI = "/xrpc/com.atproto.server.createSession"
const val BLUE_SKY_CREATE_RECORD_URI = "/xrpc/com.atproto.repo.createRecord"
const val BLUE_SKY_GET_FEED_URI = "/xrpc/xrpc/app.bsky.feed.getAuthorFeed"
const val BLUE_SKY_GET_PROFILE_URI = "/xrpc/app.bsky.actor.getProfile"

/** Client that wraps various Bluesky REST API operations. */
@SuppressFBWarnings(value = ["EI_EXPOSE_REP", "EI_EXPOSE_REP2"])
class BlueSkyClient(
private val blueSkyUrl: String,
private val publicBlueSkyUrl: String,
private val blueSkyHandle: String,
private val blueSkyPassword: String,
private val client: OkHttpClient,
Expand Down Expand Up @@ -88,10 +91,12 @@ class BlueSkyClient(
accessToken: String,
): BlueSkyCreateRecordResponse {
val body =
mapper
.writeValueAsString(
BlueSkyCreateRecordRequest(repo = blueSkyHandle, record = blueSkyRecord),
).toRequestBody("application/json; charset=utf-8".toMediaType())
replaceHandleWithDid(
requestBody =
mapper.writeValueAsString(
BlueSkyCreateRecordRequest(repo = blueSkyHandle, record = blueSkyRecord),
),
).toRequestBody("application/json; charset=utf-8".toMediaType())
val request =
Request
.Builder()
Expand All @@ -109,6 +114,33 @@ class BlueSkyClient(
}
}

/**
* Fetches the profile associated with the provided BlueSky handle.
*
* @param handle A BlueSky handle
* @return The [BlueSkyProfileResponse] containing the profile information.
* @throws IOException if unable to successfully get the profile.
*/
private fun getProfile(handle: String): BlueSkyProfileResponse {
val request =
Request
.Builder()
.url(
"$publicBlueSkyUrl$BLUE_SKY_GET_PROFILE_URI"
.toHttpUrl()
.newBuilder()
.addQueryParameter("actor", handle)
.build(),
).build()
return client.newCall(request).execute().use { response ->
if (response.isSuccessful) {
mapper.readValue(response.body!!.string(), BlueSkyProfileResponse::class.java)
} else {
throw IOException("Unexpected code $response")
}
}
}

@SuppressFBWarnings(value = ["BC_BAD_CAST_TO_ABSTRACT_COLLECTION", "SA_LOCAL_SELF_ASSIGNMENT"])
fun getPosts(accessToken: String): List<String> {
val httpUrl = "$blueSkyUrl$BLUE_SKY_GET_FEED_URI?actor=$blueSkyHandle".toHttpUrl()
Expand All @@ -130,6 +162,23 @@ class BlueSkyClient(
}
}
}

private fun replaceHandleWithDid(requestBody: String): String {
val handleToDidList =
"\"did\":\"REPLACE:([a-zA-Z0-9.-]+)\""
.toRegex()
.findAll(requestBody)
.map { matchResult ->
val handle = matchResult.groupValues[1]
val did = getProfile(handle = handle).did
Pair(handle, did)
}.toList()
var modifiedRequest = requestBody
for (pair in handleToDidList) {
modifiedRequest = modifiedRequest.replace("REPLACE:${pair.first}".toRegex(), pair.second)
}
return modifiedRequest
}
}

data class BlueSkyReplyPost(
Expand Down Expand Up @@ -257,6 +306,12 @@ data class BlueSkyCredentials(
val refreshToken: String,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class BlueSkyProfileResponse(
val did: String,
val handle: String,
)

fun BlueSkyCreateRecordResponse.toReplyPost(): BlueSkyReplyPost = BlueSkyReplyPost(uri = this.uri, cid = this.cid)

fun BlueSkyCreateSessionResponse.toBlueSkyCredentials(): BlueSkyCredentials =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class BlueSkyConfiguration {
@Qualifier("blueSkyOkhttpClient") okHttpClient: OkHttpClient,
objectMapper: ObjectMapper,
@Value("\${bluesky.url}") blueSkyUrl: String,
@Value("\${bluesky.public-url}") publicBlueSkyUrl: String,
@Value("\${bluesky.credentials.handle}") blueSkyHandle: String,
@Value("\${bluesky.credentials.password}") blueSkyPassword: String,
): BlueSkyClient =
Expand All @@ -47,5 +48,6 @@ class BlueSkyConfiguration {
blueSkyPassword = blueSkyPassword,
client = okHttpClient,
mapper = objectMapper,
publicBlueSkyUrl = publicBlueSkyUrl,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ object BlueSkyUtils {
value: String,
): BlueSkyFacetFeature =
when (type) {
BlueSkyFacetType.MENTION -> BlueSkyMentionFacetFeature(did = value)
// Adds a marker so that the real DID can be looked up and replaced later in the create record
// request body
BlueSkyFacetType.MENTION -> BlueSkyMentionFacetFeature(did = "REPLACE:$value")
BlueSkyFacetType.TAG -> BlueSkyTagFacetFeature(tag = value)
}
}
Expand Down
1 change: 1 addition & 0 deletions autobot/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ spring:
password: ${SPRING_ACTUATOR_PASSWORD}

bluesky:
public-url: ${BLUESKY_PUBLIC_URL:https://public.api.bsky.app}
url: ${BLUESKY_URL:https://bsky.social}
credentials:
handle: ${BLUESKY_HANDLE}
Expand Down
Loading

0 comments on commit 4db6392

Please sign in to comment.