This is an automated email from the ASF dual-hosted git repository. sblackmon pushed a commit to branch feat-3-nodeinfo in repository https://gitbox.apache.org/repos/asf/streams-activitypub.git
commit 80d9272d24e79ad19ffb7d6b2652863d7d46c5c1 Author: Steve Blackmon <[email protected]> AuthorDate: Mon Feb 19 16:24:26 2024 -0600 feat: x-nodeinfo2 endpoint addresses apache/streams-activitypub#3 Signed-off-by: Steve Blackmon <[email protected]> --- pom.xml | 1 + streams-activitypub-api/pom.xml | 21 +++ .../api/pojo/NodeinfoQueryResponse.json | 172 +++++++++++++++++++++ .../api/pojo/WebfingerQueryRequest.json | 2 +- .../api/pojo/WebfingerQueryResponse.json | 2 +- .../streams/activitypub/api/NodeinfoApi.scala | 9 ++ .../streams/activitypub/api/WebfingerApi.scala | 4 +- .../src/main/assembly/assembly-dist.xml | 2 +- .../graph/impl/WebfingerGraphImpl.scala | 6 +- .../src/test/resources/application.conf | 21 ++- .../test/ActivityPubGraphTestSuiteExtension.scala | 2 - .../graph/test/cases/WebfingerGraphImplTest.scala | 9 +- .../streams/activitypub/remote/NodeinfoRest.scala | 13 ++ .../streams/activitypub/remote/WebfingerRest.scala | 4 +- .../pom.xml | 51 +++--- .../server/config/NodeinfoApiStaticImplConfig.json | 12 ++ .../src/main/resources/reference.conf | 18 +++ .../activitypub/server/NodeinfoApiStaticImpl.scala | 23 +++ streams-activitypub-servlets/pom.xml | 6 + .../activitypub/servlets/NodeinfoServlet.scala | 74 +++++++++ .../activitypub/servlets/WebfingerServlet.scala | 17 +- .../servlets/test/cases/RootServletTest.scala | 2 +- streams-activitypub-utils/pom.xml | 12 ++ .../AcctPrefixResourceToResourceURISwap.scala | 2 +- .../test/scala/AcctPrefixResourceURISwapTest.scala | 4 +- streams-activitypub-webapp/pom.xml | 5 + .../src/main/webapp/WEB-INF/web.xml | 10 ++ .../webapp/test/ActivityPubWebappTestSuite.scala | 1 + .../webapp/test/cases/NodeinfoServletTest.scala | 60 +++++++ .../test/cases/ServletRegistrationTest.scala | 3 +- .../test/cases/WebappServerAvailableTest.scala | 5 +- .../webapp/test/cases/WebfingerServletTest.scala | 4 +- 32 files changed, 509 insertions(+), 68 deletions(-) diff --git a/pom.xml b/pom.xml index f778fd6..0da114e 100644 --- a/pom.xml +++ b/pom.xml @@ -421,6 +421,7 @@ <module>streams-activitypub-dist</module> <module>streams-activitypub-graph</module> <module>streams-activitypub-remote</module> + <module>streams-activitypub-server</module> <module>streams-activitypub-servlets</module> <module>streams-activitypub-utils</module> <module>streams-activitypub-webapp</module> diff --git a/streams-activitypub-api/pom.xml b/streams-activitypub-api/pom.xml index 3ffca62..cfa6952 100755 --- a/streams-activitypub-api/pom.xml +++ b/streams-activitypub-api/pom.xml @@ -63,6 +63,27 @@ </testResource> </testResources> <plugins> + <!-- tried to acquire the nodeinfo schema this way, + but the schema at the below url is invalid --> + <plugin> + <groupId>com.googlecode.maven-download-plugin</groupId> + <artifactId>download-maven-plugin</artifactId> + <executions> + <execution> + <id>download-schemas</id> + <phase>process-resources</phase> + <goals> + <goal>wget</goal> + </goals> + <configuration> + <url>https://raw.githubusercontent.com/jaywink/nodeinfo2/master/schemas/1.0/schema.json</url> + <unpack>false</unpack> + <outputDirectory>${project.build.directory}/download/jsonschema</outputDirectory> + <outputFileName>nodeinfo2.json</outputFileName> + </configuration> + </execution> + </executions> + </plugin> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> diff --git a/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/NodeinfoQueryResponse.json b/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/NodeinfoQueryResponse.json new file mode 100644 index 0000000..e49b684 --- /dev/null +++ b/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/NodeinfoQueryResponse.json @@ -0,0 +1,172 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://the-federation.info/nodeinfo2#", + "description": "NodeInfo2 schema version 1.0.", + "type": "object", + "$license": [ + "https://creativecommons.org/publicdomain/zero/1.0/" + ], + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.NodeinfoQueryResponse", + "javaInterfaces": ["java.io.Serializable"], + "properties": { + "version": { + "description": "The schema version, must be 1.0.", + "type": "string", + "required": true, + "default": "1.0" + }, + "server": { + "description": "Metadata about server in use.", + "type": "object", + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.Server", + "required": true, + "properties": { + "baseUrl": { + "description": "Base URL of the server.", + "type": "string", + "format": "uri", + "required": true + }, + "name": { + "description": "The display name of the server or service.", + "type": "string", + "required": true + }, + "software": { + "description": "The name of the server software.", + "type": "string", + "required": true + }, + "version": { + "description": "The version of this server software.", + "type": "string", + "required": true + } + } + }, + "organization": { + "description": "Metadata about the owner of this server.", + "type": "object", + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.Organization", + "name": { + "description": "The name of the organization or person.", + "type": "string" + }, + "contact": { + "description": "Contact information for the organization or person.", + "type": "string" + }, + "account": { + "description": "URL of admin or info account on this server.", + "type": "string" + } + }, + "protocols": { + "description": "The protocols supported on this server.", + "type": "array", + "minItems": 0, + "items": { + "type": "string" + } + }, + "services": { + "description": "The third party sites this server can connect to via their application API.", + "type": "object", + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.Services", + "properties": { + "inbound": { + "description": "The third party sites this server can retrieve messages from for combined display with regular traffic.", + "type": "array", + "minItems": 0, + "items": { + "type": "string" + } + }, + "outbound": { + "description": "The third party sites this server can publish messages to on the behalf of a user.", + "type": "array", + "minItems": 0, + "items": { + "type": "string" + } + } + } + }, + "openRegistrations": { + "description": "Whether this server allows open self-registration.", + "type": "boolean", + "required": true + }, + "usage": { + "description": "Usage statistics for this server.", + "type": "object", + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.Usage", + "properties": { + "users": { + "description": "statistics about the users of this server.", + "type": "object", + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.UserStatistics", + "properties": { + "total": { + "description": "The total amount of on this server registered users.", + "type": "integer", + "minimum": 0 + }, + "activeHalfyear": { + "description": "The amount of users that signed in at least once in the last 180 days.", + "type": "integer", + "minimum": 0 + }, + "activeMonth": { + "description": "The amount of users that signed in at least once in the last 30 days.", + "type": "integer", + "minimum": 0 + }, + "activeWeek": { + "description": "The amount of users that signed in at least once in the last 7 days.", + "type": "integer", + "minimum": 0 + } + } + }, + "localPosts": { + "description": "The amount of posts that were made by users that are registered on this server.", + "type": "integer", + "minimum": 0 + }, + "localComments": { + "description": "The amount of comments that were made by users that are registered on this server.", + "type": "integer", + "minimum": 0 + } + } + }, + "relay": { + "description": "State of relay integration, see https://github.com/jaywink/social-relay/blob/master/docs/relays.md", + "enum": [ + "", + "all", + "tags" + ] + }, + "otherFeatures": { + "description": "Other features of the server.", + "type": "array", + "minItems": 0, + "items": { + "type": "object", + "javaType" : "org.apache.streams.activitypub.api.pojo.nodeinfo.ServerFeature", + "properties": { + "name": { + "description": "The name of the feature", + "type": "string" + }, + "version": { + "description": "The version of the feature.", + "type": "string" + } + } + } + } + } +} diff --git a/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryRequest.json b/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryRequest.json index 507de49..1508a9d 100755 --- a/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryRequest.json +++ b/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryRequest.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema", "id": "#", "type": "object", - "javaType": "org.apache.streams.activitypub.api.pojo.WebfingerQueryRequest", + "javaType": "org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryRequest", "javaInterfaces": [ "java.io.Serializable" ], diff --git a/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryResponse.json b/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryResponse.json index cbd0c72..d8ee708 100755 --- a/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryResponse.json +++ b/streams-activitypub-api/src/main/jsonschema/org/apache/streams/activitypub/api/pojo/WebfingerQueryResponse.json @@ -5,7 +5,7 @@ "$license": [ "http://www.apache.org/licenses/LICENSE-2.0" ], - "javaType" : "org.apache.streams.activitypub.api.pojo.WebfingerQueryResponse", + "javaType" : "org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryResponse", "javaInterfaces": ["java.io.Serializable"], "properties": { "subject": { diff --git a/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/NodeinfoApi.scala b/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/NodeinfoApi.scala new file mode 100644 index 0000000..9c3fcc6 --- /dev/null +++ b/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/NodeinfoApi.scala @@ -0,0 +1,9 @@ +package org.apache.streams.activitypub.api + +import org.apache.streams.activitypub.api.pojo.nodeinfo.NodeinfoQueryResponse + +trait NodeinfoApi { + + def nodeinfoQuery: NodeinfoQueryResponse; + +} diff --git a/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/WebfingerApi.scala b/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/WebfingerApi.scala index b7cd4d7..1d5b87f 100755 --- a/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/WebfingerApi.scala +++ b/streams-activitypub-api/src/main/scala/org/apache/streams/activitypub/api/WebfingerApi.scala @@ -1,7 +1,7 @@ package org.apache.streams.activitypub.api -import org.apache.streams.activitypub.api.pojo.WebfingerQueryRequest -import org.apache.streams.activitypub.api.pojo.WebfingerQueryResponse +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryRequest +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryResponse trait WebfingerApi { diff --git a/streams-activitypub-dist/src/main/assembly/assembly-dist.xml b/streams-activitypub-dist/src/main/assembly/assembly-dist.xml index b7b062c..c7a9d5c 100644 --- a/streams-activitypub-dist/src/main/assembly/assembly-dist.xml +++ b/streams-activitypub-dist/src/main/assembly/assembly-dist.xml @@ -16,7 +16,7 @@ ~ specific language governing permissions and limitations ~ under the License. --> -<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +<assembly> <id>dist</id> <formats> <format>zip</format> diff --git a/streams-activitypub-graph/src/main/scala/org/apache/streams/activitypub/graph/impl/WebfingerGraphImpl.scala b/streams-activitypub-graph/src/main/scala/org/apache/streams/activitypub/graph/impl/WebfingerGraphImpl.scala index 6237e16..2f0e0f0 100644 --- a/streams-activitypub-graph/src/main/scala/org/apache/streams/activitypub/graph/impl/WebfingerGraphImpl.scala +++ b/streams-activitypub-graph/src/main/scala/org/apache/streams/activitypub/graph/impl/WebfingerGraphImpl.scala @@ -7,10 +7,10 @@ import org.apache.jena.riot.system.PrefixMapStd import org.apache.jena.sparql.exec.http.QueryExecutionHTTP import org.apache.jena.sparql.exec.http.QueryExecutionHTTPBuilder import org.apache.streams.activitypub.api.WebfingerApi -import org.apache.streams.activitypub.api.pojo.WebfingerQueryRequest -import org.apache.streams.activitypub.api.pojo.WebfingerQueryResponse +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryRequest +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryResponse import org.apache.streams.activitypub.graph.config.WebfingerGraphImplConfig -import org.apache.streams.activitypub.util.AcctPrefixResourceToResourceURISwap +import org.apache.streams.activitypub.utils.AcctPrefixResourceToResourceURISwap import org.apache.streams.config.ComponentConfigurator import java.net.URI diff --git a/streams-activitypub-graph/src/test/resources/application.conf b/streams-activitypub-graph/src/test/resources/application.conf index fde8a62..43e94e0 100644 --- a/streams-activitypub-graph/src/test/resources/application.conf +++ b/streams-activitypub-graph/src/test/resources/application.conf @@ -1,9 +1,28 @@ +SERVER_BASEURL = "https://mastodon.social" +NodeinfoApiStaticImplConfig = { + version = "1.0" + server = { + baseUrl = "https://example.com" + baseUrl = ${?SERVER_BASEURL} + name = "Apache Streams ActivityPub Server" + software = "apache-streams-activitypub" + version = "0.8.0-SNAPSHOT" + }, + protocols = ["activitypub"] + services = { + inbound = [] + outbound = [] + } + openRegistrations = false +} ActivityPubGraphTestSuiteExtensionConfig = { fusekiEndpointURI = "http://localhost:13330/" testDatasetResource = "doap.ttl" testDatasetId = "doap.ttl" } -WebfingerGraphImplConfig = { +BaseGraphImplConfig = { fusekiEndpointURI = "http://localhost:13330/" defaultDatasetId = "doap.ttl" } +ProfileGraphImplConfig = ${BaseGraphImplConfig} +WebfingerGraphImplConfig = ${BaseGraphImplConfig} diff --git a/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/ActivityPubGraphTestSuiteExtension.scala b/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/ActivityPubGraphTestSuiteExtension.scala index 553c729..a2273f9 100644 --- a/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/ActivityPubGraphTestSuiteExtension.scala +++ b/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/ActivityPubGraphTestSuiteExtension.scala @@ -7,8 +7,6 @@ import org.apache.jena.query.Dataset import org.apache.jena.query.DatasetFactory import org.apache.jena.riot.RDFDataMgr import org.apache.juneau.rest.client.RestClient -import org.apache.streams.activitypub.graph.config.WebfingerGraphImplConfig -import org.apache.streams.activitypub.graph.impl.WebfingerGraphImpl import org.apache.streams.activitypub.graph.test.config.ActivityPubGraphTestSuiteExtensionConfig import org.apache.streams.config.ComponentConfigurator import org.junit.jupiter.api.extension.AfterAllCallback diff --git a/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/cases/WebfingerGraphImplTest.scala b/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/cases/WebfingerGraphImplTest.scala index da05f3a..bcaf65a 100755 --- a/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/cases/WebfingerGraphImplTest.scala +++ b/streams-activitypub-graph/src/test/scala/org/apache/streams/activitypub/graph/test/cases/WebfingerGraphImplTest.scala @@ -1,12 +1,8 @@ package org.apache.streams.activitypub.graph.test.cases -import org.apache.http.HttpStatus -import org.apache.http.client.utils.URIBuilder -import org.apache.http.entity.ContentType -import org.apache.streams.activitypub.api.pojo.WebfingerQueryRequest -import org.apache.streams.activitypub.api.pojo.WebfingerQueryResponse +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryRequest +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryResponse import org.apache.streams.activitypub.graph.impl.WebfingerGraphImpl -import org.apache.streams.activitypub.graph.test.ActivityPubGraphTestSuite import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.DisplayName @@ -14,7 +10,6 @@ import org.junit.jupiter.api.Order import org.junit.jupiter.api.Test import org.slf4j.LoggerFactory -import java.nio.charset.Charset import scala.util.Try; class WebfingerGraphImplTest { diff --git a/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/NodeinfoRest.scala b/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/NodeinfoRest.scala new file mode 100644 index 0000000..786e892 --- /dev/null +++ b/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/NodeinfoRest.scala @@ -0,0 +1,13 @@ +package org.apache.streams.activitypub.remote + +import org.apache.juneau.http.remote.Remote +import org.apache.juneau.http.remote.RemoteGet +import org.apache.streams.activitypub.api.pojo.nodeinfo.NodeinfoQueryResponse + +@Remote +trait NodeinfoRest { + + @RemoteGet(path="/.well-known/x-nodeinfo2") + def nodeinfoQuery: NodeinfoQueryResponse; + +} diff --git a/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/WebfingerRest.scala b/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/WebfingerRest.scala index 01fbd29..2d81d86 100644 --- a/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/WebfingerRest.scala +++ b/streams-activitypub-remote/src/main/scala/org/apache/streams/activitypub/remote/WebfingerRest.scala @@ -4,8 +4,8 @@ import org.apache.juneau.annotation.Bean import org.apache.juneau.http.remote.Remote import org.apache.juneau.http.remote.RemoteGet import org.apache.streams.activitypub.api.WebfingerApi -import org.apache.streams.activitypub.api.pojo.WebfingerQueryRequest -import org.apache.streams.activitypub.api.pojo.WebfingerQueryResponse +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryRequest +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryResponse @Remote trait WebfingerRest extends WebfingerApi { diff --git a/streams-activitypub-api/pom.xml b/streams-activitypub-server/pom.xml similarity index 78% copy from streams-activitypub-api/pom.xml copy to streams-activitypub-server/pom.xml index 3ffca62..2595922 100755 --- a/streams-activitypub-api/pom.xml +++ b/streams-activitypub-server/pom.xml @@ -18,36 +18,33 @@ --> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - <modelVersion>4.0.0</modelVersion> - <parent> <artifactId>apache-streams-activitypub</artifactId> <groupId>org.apache.streams.activitypub</groupId> <version>0.8.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> + <modelVersion>4.0.0</modelVersion> - <artifactId>streams-activitypub-api</artifactId> - <description>Apache Streams ActivityPub Server APIs</description> + <artifactId>streams-activitypub-server</artifactId> <name>${project.artifactId}</name> + + <description>Apache Streams Server Characteristic Classes</description> + <packaging>jar</packaging> <dependencies> - + <!-- streams-activitypub-server internal dependencies --> <dependency> <groupId>org.apache.streams</groupId> <artifactId>streams-config</artifactId> <version>${project.version}</version> </dependency> - - <!-- streams-api only dependencies --> <dependency> - <groupId>org.apache.juneau</groupId> - <artifactId>juneau-rest-common</artifactId> - <version>${juneau.version}</version> + <groupId>org.apache.streams.activitypub</groupId> + <artifactId>streams-activitypub-api</artifactId> + <version>${project.version}</version> </dependency> - </dependencies> <build> <sourceDirectory>src/main/scala</sourceDirectory> @@ -63,21 +60,6 @@ </testResource> </testResources> <plugins> - <plugin> - <groupId>net.alchim31.maven</groupId> - <artifactId>scala-maven-plugin</artifactId> - <configuration> - <source>${project.basedir}/src/main/scala</source> - <scalaVersion>${scala.version}</scalaVersion> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> <plugin> <groupId>org.jsonschema2pojo</groupId> <artifactId>jsonschema2pojo-maven-plugin</artifactId> @@ -88,10 +70,25 @@ <phase>generate-sources</phase> </execution> </executions> + <dependencies> + <dependency> + <groupId>org.apache.streams.activitypub</groupId> + <artifactId>streams-activitypub-api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>jar</goal> + <goal>test-jar</goal> + </goals> + </execution> + </executions> </plugin> </plugins> </build> diff --git a/streams-activitypub-server/src/main/jsonschema/org/apache/streams/activitypub/server/config/NodeinfoApiStaticImplConfig.json b/streams-activitypub-server/src/main/jsonschema/org/apache/streams/activitypub/server/config/NodeinfoApiStaticImplConfig.json new file mode 100755 index 0000000..3c615c7 --- /dev/null +++ b/streams-activitypub-server/src/main/jsonschema/org/apache/streams/activitypub/server/config/NodeinfoApiStaticImplConfig.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "id": "#", + "type": "object", + "javaType": "org.apache.streams.activitypub.utils.config.NodeinfoApiStaticImplConfig", + "javaInterfaces": [ + "java.io.Serializable" + ], + "extends": { + "existingJavaType": "org.apache.streams.activitypub.api.pojo.nodeinfo.NodeinfoQueryResponse" + } +} diff --git a/streams-activitypub-server/src/main/resources/reference.conf b/streams-activitypub-server/src/main/resources/reference.conf new file mode 100644 index 0000000..aeeb28b --- /dev/null +++ b/streams-activitypub-server/src/main/resources/reference.conf @@ -0,0 +1,18 @@ +NodeinfoApiStaticImplConfig = { + version = "1.0" + server = { + baseUrl = "https://example.com" + baseUrl = ${?SERVER_BASEURL} + name = "Apache Streams ActivityPub Server" + software = "apache-streams-activitypub" + version = "0.8.0-SNAPSHOT" + }, + protocols = ["activitypub"] + services = { + inbound = [] + outbound = [] + } + openRegistrations = false + # usage is computed from the database + usage = {} +} diff --git a/streams-activitypub-server/src/main/scala/org/apache/streams/activitypub/server/NodeinfoApiStaticImpl.scala b/streams-activitypub-server/src/main/scala/org/apache/streams/activitypub/server/NodeinfoApiStaticImpl.scala new file mode 100644 index 0000000..730a1c1 --- /dev/null +++ b/streams-activitypub-server/src/main/scala/org/apache/streams/activitypub/server/NodeinfoApiStaticImpl.scala @@ -0,0 +1,23 @@ +package org.apache.streams.activitypub.server + +import org.apache.streams.activitypub.api.NodeinfoApi +import org.apache.streams.activitypub.api.pojo.nodeinfo.NodeinfoQueryResponse +import org.apache.streams.activitypub.utils.config.NodeinfoApiStaticImplConfig +import org.apache.streams.config.ComponentConfigurator + +object NodeinfoApiStaticImpl { + + private final val configurator: ComponentConfigurator[NodeinfoApiStaticImplConfig] = new ComponentConfigurator(classOf[NodeinfoApiStaticImplConfig]) + final val config: NodeinfoApiStaticImplConfig = configurator.detectConfiguration() + final val DEFAULT: NodeinfoApiStaticImpl = new NodeinfoApiStaticImpl(config) + +} + +class NodeinfoApiStaticImpl(config: NodeinfoApiStaticImplConfig) extends NodeinfoApi { + + override def nodeinfoQuery: NodeinfoQueryResponse = { + val response = config + response + } + +} diff --git a/streams-activitypub-servlets/pom.xml b/streams-activitypub-servlets/pom.xml index b64edf5..756cf1e 100755 --- a/streams-activitypub-servlets/pom.xml +++ b/streams-activitypub-servlets/pom.xml @@ -54,6 +54,12 @@ <artifactId>streams-activitypub-graph</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.streams.activitypub</groupId> + <artifactId>streams-activitypub-server</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.apache.juneau</groupId> <artifactId>juneau-rest-server</artifactId> diff --git a/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/NodeinfoServlet.scala b/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/NodeinfoServlet.scala new file mode 100644 index 0000000..50f59cf --- /dev/null +++ b/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/NodeinfoServlet.scala @@ -0,0 +1,74 @@ +package org.apache.streams.activitypub.servlets + +import jakarta.servlet.ServletConfig +import jakarta.servlet.http.HttpServletResponse +import jakarta.ws.rs.core.MediaType +import org.apache.juneau.html.HtmlSerializer +import org.apache.juneau.json.JsonSerializer +import org.apache.juneau.rest.RestRequest +import org.apache.juneau.rest.RestResponse +import org.apache.juneau.rest.annotation.Rest +import org.apache.juneau.rest.annotation.RestGet +import org.apache.juneau.rest.servlet.BasicRestServlet +import org.apache.streams.activitypub.api.NodeinfoApi +import org.apache.streams.activitypub.api.pojo.nodeinfo.NodeinfoQueryResponse +import org.apache.streams.activitypub.server.NodeinfoApiStaticImpl + +/** + * This is a servlet that will return the nodeinfo document for the server + */ +object NodeinfoServlet { + + final val PATH = ".well-known/x-nodeinfo2" + + given nodeinfo: NodeinfoApi = NodeinfoApiStaticImpl.DEFAULT + +} + +@Rest( + title = Array("NodeInfo Microservice"), + description = Array("NodeInfo Microservice"), + defaultAccept = MediaType.TEXT_HTML, + produces = Array( + MediaType.APPLICATION_JSON, + MediaType.TEXT_HTML, + ), + serializers = Array(classOf[JsonSerializer], classOf[HtmlSerializer]), + debugOn = "true" +) +class NodeinfoServlet extends BasicRestServlet with NodeinfoApi { + + import NodeinfoServlet.nodeinfo + + override def init(servletConfig: ServletConfig): Unit = { + super.init(servletConfig) + } + + @RestGet( + path = Array("/*"), + summary = "Request NodeInfo from server", + description = Array("Request NodeInfo from server"), + ) + def nodeinfoQueryGet(restRequest: RestRequest, + restResponse: RestResponse ) = { + try { + restResponse.setContent(nodeinfoQuery) + restResponse.setStatus(HttpServletResponse.SC_OK); + } catch { + case e: Exception => { + restResponse.setException(e); + restResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + } + + override def nodeinfoQuery: NodeinfoQueryResponse = { + doNodeinfoQuery() + } + + def doNodeinfoQuery()(using nodeinfoApi: NodeinfoApi): NodeinfoQueryResponse = { + nodeinfoApi.nodeinfoQuery + } + + +} diff --git a/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/WebfingerServlet.scala b/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/WebfingerServlet.scala index 08d97e5..d9bb10f 100755 --- a/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/WebfingerServlet.scala +++ b/streams-activitypub-servlets/src/main/scala/org/apache/streams/activitypub/servlets/WebfingerServlet.scala @@ -4,28 +4,17 @@ import jakarta.servlet.ServletConfig import jakarta.servlet.http.HttpServletResponse import jakarta.ws.rs.core.MediaType import org.apache.juneau.html.HtmlSerializer -import org.apache.juneau.http.annotation.Query import org.apache.juneau.json.JsonSerializer import org.apache.juneau.rest.RestRequest import org.apache.juneau.rest.RestResponse import org.apache.juneau.rest.annotation.Rest import org.apache.juneau.rest.annotation.RestGet -import org.apache.juneau.rest.httppart.RequestQueryParams -import org.apache.juneau.rest.matcher.RestMatcher import org.apache.juneau.rest.servlet.BasicRestServlet -import org.apache.streams.activitypub.api.pojo.Link -import org.apache.streams.activitypub.api.pojo.Properties -import org.apache.streams.activitypub.api.pojo.WebfingerQueryRequest -import org.apache.streams.activitypub.api.pojo.WebfingerQueryResponse +import org.apache.streams.activitypub.api.WebfingerApi +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryRequest +import org.apache.streams.activitypub.api.pojo.webfinger.WebfingerQueryResponse import org.apache.streams.activitypub.graph.impl.WebfingerGraphImpl import org.apache.streams.activitypub.remote.WebfingerRest -import org.apache.streams.activitypub.api.WebfingerApi - -import java.net.URI -import java.util.ArrayList -import java.util.Optional -import scala.collection.mutable.Buffer -import scala.util.Try /** * org.apache.streams.activitypub.servlets.WebfingerResource response to inquires about any URI, typically an item in one of the published feeds, or one of the diff --git a/streams-activitypub-servlets/src/test/scala/org/apache/streams/activitypub/servlets/test/cases/RootServletTest.scala b/streams-activitypub-servlets/src/test/scala/org/apache/streams/activitypub/servlets/test/cases/RootServletTest.scala index 2a70ab9..42971d8 100755 --- a/streams-activitypub-servlets/src/test/scala/org/apache/streams/activitypub/servlets/test/cases/RootServletTest.scala +++ b/streams-activitypub-servlets/src/test/scala/org/apache/streams/activitypub/servlets/test/cases/RootServletTest.scala @@ -10,9 +10,9 @@ import org.apache.juneau.dto.html5.Head import org.apache.juneau.dto.html5.Html import org.apache.juneau.rest.mock.MockRestClient import org.apache.streams.activitypub.servlets.RootServlet +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.junit.jupiter.api.Assertions import scala.Option import scala.util.Try diff --git a/streams-activitypub-utils/pom.xml b/streams-activitypub-utils/pom.xml index 51fcf92..73be40e 100755 --- a/streams-activitypub-utils/pom.xml +++ b/streams-activitypub-utils/pom.xml @@ -34,6 +34,18 @@ <packaging>jar</packaging> <dependencies> + <!-- streams-activitypub-utils internal dependencies --> + <dependency> + <groupId>org.apache.streams</groupId> + <artifactId>streams-config</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.streams.activitypub</groupId> + <artifactId>streams-activitypub-api</artifactId> + <version>${project.version}</version> + </dependency> + <!-- streams-activitypub-utils common dependencies --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> diff --git a/streams-activitypub-utils/src/main/scala/org/apache/streams/activitypub/utils/AcctPrefixResourceToResourceURISwap.scala b/streams-activitypub-utils/src/main/scala/org/apache/streams/activitypub/utils/AcctPrefixResourceToResourceURISwap.scala index 4c30cce..28c9413 100644 --- a/streams-activitypub-utils/src/main/scala/org/apache/streams/activitypub/utils/AcctPrefixResourceToResourceURISwap.scala +++ b/streams-activitypub-utils/src/main/scala/org/apache/streams/activitypub/utils/AcctPrefixResourceToResourceURISwap.scala @@ -1,4 +1,4 @@ -package org.apache.streams.activitypub.util +package org.apache.streams.activitypub.utils import org.apache.http.client.utils.URIBuilder import org.apache.juneau.BeanSession diff --git a/streams-activitypub-utils/src/test/scala/AcctPrefixResourceURISwapTest.scala b/streams-activitypub-utils/src/test/scala/AcctPrefixResourceURISwapTest.scala index 2dbfc9f..cac34dd 100644 --- a/streams-activitypub-utils/src/test/scala/AcctPrefixResourceURISwapTest.scala +++ b/streams-activitypub-utils/src/test/scala/AcctPrefixResourceURISwapTest.scala @@ -1,7 +1,7 @@ -package org.apache.streams.activitypub.util.test.cases +package org.apache.streams.activitypub.utils.test.cases import org.apache.http.client.utils.URIBuilder -import org.apache.streams.activitypub.util.AcctPrefixResourceToResourceURISwap +import org.apache.streams.activitypub.utils.AcctPrefixResourceToResourceURISwap import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test diff --git a/streams-activitypub-webapp/pom.xml b/streams-activitypub-webapp/pom.xml index e9d466d..d5d637a 100755 --- a/streams-activitypub-webapp/pom.xml +++ b/streams-activitypub-webapp/pom.xml @@ -56,6 +56,11 @@ <artifactId>streams-activitypub-graph</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.streams.activitypub</groupId> + <artifactId>streams-activitypub-server</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.streams.activitypub</groupId> <artifactId>streams-activitypub-servlets</artifactId> diff --git a/streams-activitypub-webapp/src/main/webapp/WEB-INF/web.xml b/streams-activitypub-webapp/src/main/webapp/WEB-INF/web.xml index 788d453..c1c8baf 100644 --- a/streams-activitypub-webapp/src/main/webapp/WEB-INF/web.xml +++ b/streams-activitypub-webapp/src/main/webapp/WEB-INF/web.xml @@ -42,4 +42,14 @@ <servlet-name>webfinger</servlet-name> <url-pattern>/.well-known/webfinger</url-pattern> </servlet-mapping> + <servlet> + <display-name>NodeinfoServlet</display-name> + <servlet-name>nodeinfo2</servlet-name> + <servlet-class>org.apache.streams.activitypub.servlets.NodeinfoServlet</servlet-class> + <load-on-startup>5</load-on-startup> + </servlet> + <servlet-mapping> + <servlet-name>nodeinfo2</servlet-name> + <url-pattern>/.well-known/x-nodeinfo2</url-pattern> + </servlet-mapping> </web-app> diff --git a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/ActivityPubWebappTestSuite.scala b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/ActivityPubWebappTestSuite.scala index 8efba57..2a3224b 100755 --- a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/ActivityPubWebappTestSuite.scala +++ b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/ActivityPubWebappTestSuite.scala @@ -27,6 +27,7 @@ object ActivityPubWebappTestSuite { classOf[org.apache.streams.activitypub.webapp.test.cases.WebappServerAvailableTest], classOf[org.apache.streams.activitypub.webapp.test.cases.ServletRegistrationTest], classOf[org.apache.streams.activitypub.webapp.test.cases.RootServletTest], + classOf[org.apache.streams.activitypub.webapp.test.cases.NodeinfoServletTest], classOf[org.apache.streams.activitypub.webapp.test.cases.WebfingerServletTest] )) class ActivityPubWebappTestSuite { diff --git a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/NodeinfoServletTest.scala b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/NodeinfoServletTest.scala new file mode 100755 index 0000000..0d62b53 --- /dev/null +++ b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/NodeinfoServletTest.scala @@ -0,0 +1,60 @@ +package org.apache.streams.activitypub.webapp.test.cases + +import org.apache.http.HttpHeaders +import org.apache.http.HttpStatus +import org.apache.http.client.utils.URIBuilder +import org.apache.http.entity.ContentType +import org.apache.streams.activitypub.servlets.NodeinfoServlet +import org.apache.streams.activitypub.webapp.test.ActivityPubWebappTestSuiteExtension +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Order +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.slf4j.LoggerFactory + +import java.nio.charset.Charset; + +@ExtendWith(Array(classOf[ActivityPubWebappTestSuiteExtension])) +class NodeinfoServletTest(using helper: ActivityPubWebappTestSuiteExtension) { + + private final val LOGGER = LoggerFactory.getLogger(classOf[NodeinfoServletTest]); + + private val uriBuilder : URIBuilder = helper.uriBuilder + .setCharset(Charset.defaultCharset()) + .setPath(NodeinfoServlet.PATH) + + /** + * Request NodeinfoQueryResponse as HTML + */ + @DisplayName("NodeinfoQuery as HTML") + @Order(5) + @Test + def testNodeinfoQueryAsHtml(using helper: ActivityPubWebappTestSuiteExtension): Unit = { + val request = helper.restClientBuilder + .accept(ContentType.TEXT_HTML.getMimeType) + .json() + .build() + .get(uriBuilder.build()) + val response = request.run() + response.assertStatus(HttpStatus.SC_OK) + response.assertHeader(HttpHeaders.CONTENT_TYPE).isContains(ContentType.TEXT_HTML.getMimeType) + } + + /** + * Request NodeinfoQueryResponse as Json + */ + @DisplayName("NodeinfoQuery as Json") + @Order(5) + @Test + def testNodeinfoQueryAsJson(using helper: ActivityPubWebappTestSuiteExtension) : Unit = { + val request = helper.restClientBuilder + .accept(ContentType.APPLICATION_JSON.getMimeType) + .json() + .build() + .get(uriBuilder.build()) + val response = request.run() + response.assertStatus(HttpStatus.SC_OK) + response.assertHeader(HttpHeaders.CONTENT_TYPE).isContains(ContentType.APPLICATION_JSON.getMimeType) + } + +} diff --git a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/ServletRegistrationTest.scala b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/ServletRegistrationTest.scala index 1ee50ee..abffa19 100755 --- a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/ServletRegistrationTest.scala +++ b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/ServletRegistrationTest.scala @@ -23,7 +23,8 @@ class ServletRegistrationTest { def testAllServletsRegistered(using helper: ActivityPubWebappTestSuiteExtension) : Unit = { val servletRegistrations = helper.context.getServletContext.getServletRegistrations() Assertions.assertNotNull(servletRegistrations) - Assertions.assertEquals(2, servletRegistrations.size()) + Assertions.assertEquals(3, servletRegistrations.size()) + Assertions.assertTrue(servletRegistrations.containsKey("nodeinfo2")) Assertions.assertTrue(servletRegistrations.containsKey("root")) Assertions.assertTrue(servletRegistrations.containsKey("webfinger")) } diff --git a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebappServerAvailableTest.scala b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebappServerAvailableTest.scala index 1d79ca5..8c0e504 100755 --- a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebappServerAvailableTest.scala +++ b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebappServerAvailableTest.scala @@ -1,14 +1,17 @@ package org.apache.streams.activitypub.webapp.test.cases +import org.apache.streams.activitypub.webapp.test.ActivityPubWebappTestSuite +import org.apache.streams.activitypub.webapp.test.ActivityPubWebappTestSuiteExtension import org.awaitility.Awaitility.await import org.awaitility.Awaitility.waitAtMost import org.awaitility.core.ConditionTimeoutException -import org.apache.streams.activitypub.webapp.test.ActivityPubWebappTestSuite import org.awaitility.scala.AwaitilitySupport import org.hamcrest.MatcherAssert.assertThat import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Order import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith import org.slf4j.LoggerFactory import scala.concurrent.duration.MINUTES diff --git a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebfingerServletTest.scala b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebfingerServletTest.scala index 2478ccb..ee4f0db 100755 --- a/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebfingerServletTest.scala +++ b/streams-activitypub-webapp/src/test/scala/org/apache/streams/activitypub/webapp/test/cases/WebfingerServletTest.scala @@ -4,8 +4,10 @@ import org.apache.http.HttpStatus import org.apache.http.client.utils.URIBuilder import org.apache.http.entity.ContentType import org.apache.streams.activitypub.servlets.WebfingerServlet +import org.apache.streams.activitypub.webapp.test.ActivityPubWebappTestSuite import org.apache.streams.activitypub.webapp.test.ActivityPubWebappTestSuiteExtension import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Order import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.slf4j.Logger @@ -18,7 +20,7 @@ class WebfingerServletTest(using helper: ActivityPubWebappTestSuiteExtension) { private final val LOGGER = LoggerFactory.getLogger(classOf[WebfingerServletTest]); - private val uriBuilder : URIBuilder = ActivityPubWebappTestSuite.helper.uriBuilder + private val uriBuilder : URIBuilder = helper.uriBuilder .setCharset(Charset.defaultCharset()) .setPath(WebfingerServlet.PATH)
