Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
<module>tika-langdetect</module>
<module>tika-pipes</module>

<module>tika-grpc-api</module>
<module>tika-grpc-mapper</module>
<module>tika-grpc</module>
<module>tika-app</module>
<module>tika-server</module>
Expand Down
15 changes: 15 additions & 0 deletions tika-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,21 @@
<artifactId>tika-async-cli</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-grpc-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-grpc-mapper</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-grpc</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-httpclient-commons</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void testParseContextJson() throws Exception {
Assertions.assertEquals("PARSE_SUCCESS", htmlReply.getStatus(),
"Parse should succeed with HTML handler type");

String htmlContent = htmlReply.getFieldsMap().get("X-TIKA:content");
String htmlContent = htmlReply.getDocument().getMarkdown();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only change anywhere in tika-e2e-tests/tika-grpc for this PR (this line and the analogous one at line 334 for the TEXT handler). It's a compile-fix that keeps the pre-existing "content is non-empty" assertion alive against the new API, but it does not exercise the new typed contract through the live gRPC server at all.

None of the e2e tests (HandlerTypeTest, FileSystemFetcherTest, IgniteConfigStoreTest, ExternalTestBase) assert on:

  • document.metadata (typed title/authors/dates/counts)
  • document.extra (tagged-tail typing: int/bool/date/string)
  • document.blocks (structured content tree: headings/tables/lists/code)
  • document.embedded (recursion for container formats, e.g. an Office doc with an embedded image)
  • document.format_category

FileSystemFetcherTest/IgniteConfigStoreTest/ExternalTestBase only ever inspect getFetchKey()/getStatus() on FetchAndParseReply and never call getDocument() at all, so the bulk-corpus/streaming/ignite e2e paths give zero signal on the new contract this PR introduces.

The real proof of correctness lives entirely in tika-grpc-mapper's unit tests, which feed fixture-derived Metadata/markdown into DocumentBuilder in-process -- good for the mapping logic, but it bypasses the actual gRPC wire serialization, the live server (TikaGrpcServerImpl), the pipes client, and fetcher plumbing entirely.

Requesting: add (or extend this test) at least one e2e case per format that fetches a real file through the live server and asserts on a couple of document.metadata fields, at least one document.extra tagged key, and one document.embedded case -- not just that markdown is non-empty.

Assertions.assertNotNull(htmlContent, "Content should be present in HTML response");
log.info("HTML content (first 200 chars): {}", htmlContent.substring(0, Math.min(200, htmlContent.length())));
Assertions.assertTrue(
Expand All @@ -331,7 +331,7 @@ void testParseContextJson() throws Exception {
Assertions.assertEquals("PARSE_SUCCESS", textReply.getStatus(),
"Parse should succeed with TEXT handler type");

String textContent = textReply.getFieldsMap().get("X-TIKA:content");
String textContent = textReply.getDocument().getMarkdown();
Assertions.assertNotNull(textContent, "Content should be present in text response");
log.info("Text content (first 200 chars): {}", textContent.substring(0, Math.min(200, textContent.length())));
Assertions.assertFalse(
Expand Down
55 changes: 55 additions & 0 deletions tika-grpc-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Apache Tika gRPC API

Typed protobuf messages for Tika parse output under `org.apache.tika.grpc.v1`.

## Contents

- **Document** (`document.proto`) — the single, small, stable parse-result contract: a
structured markdown content tree (`blocks`/`markdown`), typed common metadata
(`DocumentMetadata`), and a tagged metadata tail (`extra`) for everything
format-specific.
- **Bundled descriptors** — `META-INF/org.apache.tika.grpc.v1.descriptors` in the
published jar.

## Usage

```xml
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-grpc-api</artifactId>
<version>${tika.version}</version>
</dependency>
```

## The Document shape

Rather than one proto message per source format, `Document` models content and
metadata by *concern*, not by *format*:

- **Content** lives in `markdown` (the authoritative render) and `blocks` (the same
content parsed into a structured tree of headings/paragraphs/lists/tables/code
blocks/inline runs). This is format-agnostic: every Tika parser's output reaches
this shape the same way, via markdown.
- **Metadata** has a small, bounded set of typed common fields on `DocumentMetadata`
(title, authors, description, keywords, languages, dates, counts, dimensions,
rights) plus a tagged tail (`extra`, a `repeated MetadataField`) for everything
format-specific. Tail values are typed where Tika's own `Property` declares a type
(integer/number/boolean/timestamp), and a string otherwise — never guessed.
- **`format_category`** is a cheap routing hint (`FormatCategory` enum: PDF, OFFICE,
IMAGE, HTML, RTF, EPUB, WARC, GENERIC). It is not mutually exclusive with anything
in `extra` — a document can be `FORMAT_CATEGORY_PDF` and still carry Creative
Commons rights metadata, for example.
- **`embedded`** recurses: a PDF with an embedded image is one `Document` whose
`embedded` list contains a fully-typed child `Document` for the image.

Format-specific mapping (which Tika `Property` becomes which typed field, and what
falls through to `extra`) lives in `tika-grpc-mapper`'s
`org.apache.tika.grpc.mapper.transform.DocumentTransformer` implementations, one per
format — code, not schema. Adding a parser means adding a transformer; the wire
contract does not change.

## Lint

```bash
cd tika-grpc-api && buf lint
```
29 changes: 29 additions & 0 deletions tika-grpc-api/buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

version: v2
modules:
- path: src/main/proto
name: buf.build/apache/tika-grpc-api
deps:
- buf.build/googleapis/googleapis
lint:
use:
- STANDARD
breaking:
use:
- FILE
121 changes: 121 additions & 0 deletions tika-grpc-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tika-grpc-api</artifactId>
<packaging>jar</packaging>
<name>Apache Tika gRPC API (typed Document protos)</name>
<url>https://tika.apache.org/</url>

<parent>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parent</artifactId>
<version>${revision}</version>
<relativePath>../tika-parent/pom.xml</relativePath>
</parent>

<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
</dependencies>

<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
<includes>
<include>org/apache/tika/grpc/v1/*.proto</include>
</includes>
<writeDescriptorSet>true</writeDescriptorSet>
<includeDependenciesInDescriptorSet>true</includeDependenciesInDescriptorSet>
<descriptorSetOutputDirectory>${project.build.outputDirectory}/META-INF</descriptorSetOutputDirectory>
<descriptorSetFileName>org.apache.tika.grpc.v1.descriptors</descriptorSetFileName>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>add-protobuf-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/protobuf/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.apache.tika.grpc.api</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<excludeGeneratedSources>true</excludeGeneratedSources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<inputExcludes>
<inputExclude>README.md</inputExclude>
</inputExcludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading
Loading