mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 22:06:43 +01:00
- Fixes caching bug of NIP-96 servers
- Allow NIP-96 setups with relative URLs - Displays error messages if the server has sent in the body - Adds a test case for both
This commit is contained in:
24
.idea/inspectionProfiles/Project_Default.xml
generated
24
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +1,30 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
|
|||||||
@@ -188,6 +188,12 @@ class ImageUploadTesting {
|
|||||||
testBase(Nip96MediaServers.ServerName("nostpic.com", "https://nostpic.com"))
|
testBase(Nip96MediaServers.ServerName("nostpic.com", "https://nostpic.com"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException::class)
|
||||||
|
fun testSprovoostNl() =
|
||||||
|
runBlocking {
|
||||||
|
testBase(Nip96MediaServers.ServerName("sprovoost.nl", "https://img.sprovoost.nl/"))
|
||||||
|
}
|
||||||
|
|
||||||
@Test()
|
@Test()
|
||||||
@Ignore("Not Working anymore")
|
@Ignore("Not Working anymore")
|
||||||
fun testNostrOnch() =
|
fun testNostrOnch() =
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.ammolite.service.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
object Nip96MediaServers {
|
object Nip96MediaServers {
|
||||||
val DEFAULT =
|
val DEFAULT =
|
||||||
@@ -76,10 +78,26 @@ class Nip96Retriever {
|
|||||||
val mediaTransformations: Map<MimeType, Array<String>> = emptyMap(),
|
val mediaTransformations: Map<MimeType, Array<String>> = emptyMap(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun parse(body: String): ServerInfo {
|
fun parse(
|
||||||
|
baseUrl: String,
|
||||||
|
body: String,
|
||||||
|
): ServerInfo {
|
||||||
val mapper =
|
val mapper =
|
||||||
jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
return mapper.readValue(body, ServerInfo::class.java)
|
val serverInfo = mapper.readValue(body, ServerInfo::class.java)
|
||||||
|
|
||||||
|
return serverInfo.copy(
|
||||||
|
apiUrl = makeAbsoluteIfRelativeUrl(baseUrl, serverInfo.apiUrl),
|
||||||
|
downloadUrl = serverInfo.downloadUrl?.let { makeAbsoluteIfRelativeUrl(baseUrl, it) },
|
||||||
|
delegatedToUrl = serverInfo.delegatedToUrl?.let { makeAbsoluteIfRelativeUrl(baseUrl, it) },
|
||||||
|
tosUrl = serverInfo.tosUrl?.let { makeAbsoluteIfRelativeUrl(baseUrl, it) },
|
||||||
|
plans =
|
||||||
|
serverInfo.plans.mapValues { u ->
|
||||||
|
u.value.copy(
|
||||||
|
url = u.value.url?.let { makeAbsoluteIfRelativeUrl(baseUrl, it) },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadInfo(baseUrl: String): ServerInfo {
|
suspend fun loadInfo(baseUrl: String): ServerInfo {
|
||||||
@@ -98,7 +116,7 @@ class Nip96Retriever {
|
|||||||
val body = it.body.string()
|
val body = it.body.string()
|
||||||
try {
|
try {
|
||||||
if (it.isSuccessful) {
|
if (it.isSuccessful) {
|
||||||
return parse(body)
|
return parse(baseUrl, body)
|
||||||
} else {
|
} else {
|
||||||
throw RuntimeException(
|
throw RuntimeException(
|
||||||
"Resulting Message from $baseUrl is an error: ${response.code} ${response.message}",
|
"Resulting Message from $baseUrl is an error: ${response.code} ${response.message}",
|
||||||
@@ -117,3 +135,18 @@ class Nip96Retriever {
|
|||||||
typealias PlanName = String
|
typealias PlanName = String
|
||||||
|
|
||||||
typealias MimeType = String
|
typealias MimeType = String
|
||||||
|
|
||||||
|
fun makeAbsoluteIfRelativeUrl(
|
||||||
|
baseUrl: String,
|
||||||
|
potentialyRelativeUrl: String,
|
||||||
|
): String =
|
||||||
|
try {
|
||||||
|
val apiUrl = URI(potentialyRelativeUrl)
|
||||||
|
if (apiUrl.isAbsolute) {
|
||||||
|
potentialyRelativeUrl
|
||||||
|
} else {
|
||||||
|
URL(URL(baseUrl), potentialyRelativeUrl).toString()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
potentialyRelativeUrl
|
||||||
|
}
|
||||||
|
|||||||
@@ -190,8 +190,26 @@ class Nip96Uploader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
val msg = response.body.string()
|
||||||
|
|
||||||
|
val errorMessage =
|
||||||
|
try {
|
||||||
|
val tree = jacksonObjectMapper().readTree(msg)
|
||||||
|
val status = tree.get("status")?.asText()
|
||||||
|
val message = tree.get("message")?.asText()
|
||||||
|
if (status == "error" && message != null) {
|
||||||
|
message
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val explanation = HttpStatusMessages.resourceIdFor(response.code)
|
val explanation = HttpStatusMessages.resourceIdFor(response.code)
|
||||||
if (explanation != null) {
|
if (errorMessage != null) {
|
||||||
|
throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, errorMessage))
|
||||||
|
} else if (explanation != null) {
|
||||||
throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, stringRes(context, explanation)))
|
throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, stringRes(context, explanation)))
|
||||||
} else {
|
} else {
|
||||||
throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, response.code))
|
throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, response.code))
|
||||||
|
|||||||
@@ -1654,7 +1654,17 @@ fun ImageVideoDescription(
|
|||||||
fileServers.map { TitleExplainer(it.server.name, it.server.baseUrl) }.toImmutableList()
|
fileServers.map { TitleExplainer(it.server.name, it.server.baseUrl) }.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedServer by remember { mutableStateOf(ServerOption(defaultServer, false)) }
|
var selectedServer by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
ServerOption(
|
||||||
|
fileServers
|
||||||
|
.firstOrNull { it.server == defaultServer }
|
||||||
|
?.server
|
||||||
|
?: fileServers[0].server,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
var message by remember { mutableStateOf("") }
|
var message by remember { mutableStateOf("") }
|
||||||
var sensitiveContent by remember { mutableStateOf(false) }
|
var sensitiveContent by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -1778,7 +1788,7 @@ fun ImageVideoDescription(
|
|||||||
label = stringRes(id = R.string.file_server),
|
label = stringRes(id = R.string.file_server),
|
||||||
placeholder =
|
placeholder =
|
||||||
fileServers
|
fileServers
|
||||||
.firstOrNull { it.server == accountViewModel.account.settings.defaultFileServer }
|
.firstOrNull { it.server == defaultServer }
|
||||||
?.server
|
?.server
|
||||||
?.name
|
?.name
|
||||||
?: fileServers[0].server.name,
|
?: fileServers[0].server.name,
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ fun TextSpinner(
|
|||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
var optionsShowing by remember { mutableStateOf(false) }
|
var optionsShowing by remember { mutableStateOf(false) }
|
||||||
var currentText by remember { mutableStateOf(placeholder) }
|
var currentText by remember(placeholder) { mutableStateOf(placeholder) }
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|||||||
@@ -24,6 +24,26 @@ import junit.framework.TestCase.assertEquals
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class Nip96Test {
|
class Nip96Test {
|
||||||
|
val relativeUrlTest =
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"api_url": "/n96",
|
||||||
|
"download_url": "/",
|
||||||
|
"content_types": [
|
||||||
|
"image/*",
|
||||||
|
"video/*",
|
||||||
|
"audio/*"
|
||||||
|
],
|
||||||
|
"plans": {
|
||||||
|
"free": {
|
||||||
|
"name": "",
|
||||||
|
"is_nip98_required": true,
|
||||||
|
"max_byte_size": 5000000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
val json =
|
val json =
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
@@ -116,7 +136,7 @@ class Nip96Test {
|
|||||||
|
|
||||||
@Test()
|
@Test()
|
||||||
fun parseNostrBuild() {
|
fun parseNostrBuild() {
|
||||||
val info = Nip96Retriever().parse(json)
|
val info = Nip96Retriever().parse("https://nostr.build", json)
|
||||||
|
|
||||||
assertEquals("https://nostr.build/api/v2/nip96/upload", info.apiUrl)
|
assertEquals("https://nostr.build/api/v2/nip96/upload", info.apiUrl)
|
||||||
assertEquals("https://media.nostr.build", info.downloadUrl)
|
assertEquals("https://media.nostr.build", info.downloadUrl)
|
||||||
@@ -142,4 +162,14 @@ class Nip96Test {
|
|||||||
assertEquals(26843545600L, info.plans["creator"]?.maxByteSize)
|
assertEquals(26843545600L, info.plans["creator"]?.maxByteSize)
|
||||||
assertEquals(10737418240L, info.plans["professional"]?.maxByteSize)
|
assertEquals(10737418240L, info.plans["professional"]?.maxByteSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
fun parseRelativeUrls() {
|
||||||
|
val info = Nip96Retriever().parse("https://test.com", relativeUrlTest)
|
||||||
|
|
||||||
|
assertEquals("https://test.com/n96", info.apiUrl)
|
||||||
|
assertEquals("https://test.com/", info.downloadUrl)
|
||||||
|
assertEquals(null, info.tosUrl)
|
||||||
|
assertEquals(null, info.delegatedToUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user