Skip to content

Commit

Permalink
Merge pull request #376 from FgForrest/dev
Browse files Browse the repository at this point in the history
Release: evitaLab Christrmas update, evitaQL escape characters, ...
  • Loading branch information
lukashornych authored Dec 19, 2023
2 parents bf51fe8 + 6aacd29 commit 5542a88
Show file tree
Hide file tree
Showing 53 changed files with 1,577 additions and 1,561 deletions.
16 changes: 8 additions & 8 deletions documentation/user/en/use/api/query-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ you can still use the `enrichEntity` method on the
will be fully fetched again. However, we plan to optimize this scenario in the future.
</Note>

### Custom contracts
## Custom contracts

Data retrieved from evitaDB is represented by the internal evitaDB data structures, which use the domain names
associated with the evitaDB representation. You may want to use your own domain names and data structures in your
Expand Down Expand Up @@ -296,7 +296,7 @@ returned as a result of reference getter calls, where you don't need the full-fl

Methods annotated with these annotations must follow the expected method signature conventions:

#### Primary key
### Primary key

In order to access the primary key of the entity, you must use number data type (usually
[int](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html)) and annotate it with the <SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/annotation/PrimaryKey.java</SourceClass>
Expand All @@ -308,7 +308,7 @@ or <SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/anno

</SourceAlternativeTabs>

#### Attributes
### Attributes

To access the entity or reference attribute, you must use the appropriate data type and annotate it with the
<SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/annotation/Attribute.java</SourceClass>
Expand Down Expand Up @@ -342,7 +342,7 @@ call may fail with a `NullPointerException` if the data wasn't fetched even thou

</Note>

#### Associated data
### Associated data

To access the entity or reference associated data, you must use the appropriate data type and annotate it with
the <SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/annotation/AssociatedData.java</SourceClass>
Expand All @@ -364,7 +364,7 @@ or [Set](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/
If the method returns ["non-supported data type"](../data-types.md#simple-data-types) evitaDB automatically converts the data
from ["complex data type"](../data-types.md#complex-data-types) using [documented deserialization rules](../data-types.md#deserialization).

#### Prices
### Prices

To access the entity prices, you must always work with
<SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/PriceContract.java</SourceClass> data type
Expand All @@ -388,7 +388,7 @@ If the method can return multiple prices, you need to wrap it in [Collection](ht
The method may return null if the entity is a root entity. Therefore, it's not recommended to use primitive data types,
because the method call may fail with a `NullPointerException` in such a case.

#### Hierarchy
### Hierarchy

To access the hierarchy placement information of the entity (i.e., its parent), you must use either the numeric data
type, your own custom interface type, <SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/SealedEntity.java</SourceClass>
Expand All @@ -408,7 +408,7 @@ or [OptionalLong](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/j
The method may return null if the entity is a root entity. Therefore, it's not recommended to use primitive data types,
because the method call may fail with a `NullPointerException` in such a case.

#### References
### References

To access the references of the entity, you must use either the numeric data type, your own custom interface type,
<SourceClass>evita_api/src/main/java/io/evitadb/api/requestResponse/data/EntityReferenceContract.java</SourceClass>
Expand Down Expand Up @@ -441,7 +441,7 @@ Methods annotated with this annotation should respect the cardinality of the ref
`EXACTLY_ONE` or `ZERO_OR_ONE`, the method should directly return the entity or the reference to it. If the cardinality
is `ZERO_OR_MORE` or `ONE_OR_MORE`, the method should return a collection or array of entities or references to them.

#### Access to evitaDB data structures
### Access to evitaDB data structures

Your read contract can implement the following interfaces to access the underlying evitaDB data structures:

Expand Down
376 changes: 188 additions & 188 deletions documentation/user/en/use/api/write-data.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,6 @@
session.createNewEntity(
ProductEditor.class, 100
)

/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023
*
* Licensed under the Business Source License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/main/LICENSE
*
* 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.
*/

// fill the data
.setCode("JP328a01a")
.setName("Creative OUTLIER FREE PRO", Locale.ENGLISH)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023
*
* Licensed under the Business Source License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/main/LICENSE
*
* 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.
*/

@Entity(
allowedEvolution = {
EvolutionMode.ADDING_LOCALES,
Expand Down
88 changes: 88 additions & 0 deletions evita_common/src/main/java/io/evitadb/utils/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,94 @@ public static String rightPad(@Nonnull String theText, @Nonnull String padCharac
return theText + padCharacter.repeat(Math.max(0, requestedSize - theText.length()));
}

/**
* Returns a string whose value is the passed string, with escape sequences
* translated as if in a string literal.
* Base code borrowed from {@link String#translateEscapes()} and extended to support unicode escape sequences.
*
* @throws IllegalArgumentException when an escape sequence is malformed.
*
* @return String with escape sequences translated.
*/
public static String translateEscapes(@Nonnull String s) {
if (s.isEmpty()) {
return "";
}
char[] chars = s.toCharArray();
int length = chars.length;
int from = 0;
int to = 0;
while (from < length) {
char[] ch = { chars[from++] };
if (ch[0] == '\\') {
ch[0] = from < length ? chars[from++] : '\0';
switch (ch[0]) {
case 'b':
ch[0] = '\b';
break;
case 'f':
ch[0] = '\f';
break;
case 'n':
ch[0] = '\n';
break;
case 'r':
ch[0] = '\r';
break;
case 's':
ch[0] = ' ';
break;
case 't':
ch[0] = '\t';
break;
case 'u':
final int unicodeCodepoint = Integer.parseInt(new String(chars, from, 4), 16);
ch = Character.toChars(unicodeCodepoint);
from += 4;
break;
case '\'':
case '\"':
case '\\':
// as is
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
int limit = Integer.min(from + (ch[0] <= '3' ? 2 : 1), length);
int code = ch[0] - '0';
while (from < limit) {
ch[0] = chars[from];
if (ch[0] < '0' || '7' < ch[0]) {
break;
}
from++;
code = (code << 3) | (ch[0] - '0');
}
ch[0] = (char)code;
break;
case '\n':
continue;
case '\r':
if (from < length && chars[from] == '\n') {
from++;
}
continue;
default: {
String msg = String.format(
"Invalid escape sequence: \\%c \\\\u%04X",
ch[0], (int)ch[0]);
throw new IllegalArgumentException(msg);
}
}
}

for (char c : ch) {
chars[to++] = c;
}
}

return new String(chars, 0, to);
}

/**
* <p>
* Replaces all occurrences of Strings within another String.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,17 @@ protected Optional<EvitaSessionContract> createSession(@Nonnull RestEndpointExch

final Map<String, Object> parameters = getParametersFromRequest(exchange);
final String catalogName = (String) parameters.get(CatalogsHeaderDescriptor.NAME.name());
final CatalogContract catalog = restApiHandlingContext.getEvita().getCatalogInstance(catalogName)
final CatalogContract catalog = restHandlingContext.getEvita().getCatalogInstance(catalogName)
.orElseThrow(() -> new RestInvalidArgumentException("Catalog `" + catalogName + "` does not exist."));

if (modifiesData()) {
final EvitaSessionContract session = restApiHandlingContext.getEvita().createReadWriteSession(catalog.getName());
final EvitaSessionContract session = restHandlingContext.getEvita().createReadWriteSession(catalog.getName());
if (catalog.supportsTransaction()) {
session.openTransaction();
}
return Optional.of(session);
} else {
return Optional.of(restApiHandlingContext.getEvita().createReadOnlySession(catalog.getName()));
return Optional.of(restHandlingContext.getEvita().createReadOnlySession(catalog.getName()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ListCatalogsHandler(@Nonnull LabApiHandlingContext restApiHandlingContext
@Nonnull
@Override
protected EndpointResponse doHandleRequest(@Nonnull RestEndpointExchange exchange) {
final Collection<CatalogContract> catalogs = restApiHandlingContext.getEvita().getCatalogs();
final Collection<CatalogContract> catalogs = restHandlingContext.getEvita().getCatalogs();
return new SuccessEndpointResponse(convertResultIntoSerializableObject(exchange, catalogs));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ public class QueryEntitiesHandler extends JsonRestHandler<LabApiHandlingContext>
@Nonnull private final GenericEntityJsonSerializer entityJsonSerializer;
@Nonnull private final ExtraResultsJsonSerializer extraResultsJsonSerializer;

public QueryEntitiesHandler(@Nonnull LabApiHandlingContext restApiHandlingContext) {
super(restApiHandlingContext);
public QueryEntitiesHandler(@Nonnull LabApiHandlingContext restHandlingContext) {
super(restHandlingContext);
this.queryParser = new DefaultQueryParser();
this.entityJsonSerializer = new GenericEntityJsonSerializer(restApiHandlingContext);
this.entityJsonSerializer = new GenericEntityJsonSerializer(restHandlingContext.getObjectMapper());
this.extraResultsJsonSerializer = new ExtraResultsJsonSerializer(
restApiHandlingContext,
this.entityJsonSerializer,
StringUtils::toCamelCase
StringUtils::toCamelCase,
this.restHandlingContext.getObjectMapper()
);
}

Expand All @@ -87,10 +87,10 @@ public QueryEntitiesHandler(@Nonnull LabApiHandlingContext restApiHandlingContex
protected Optional<EvitaSessionContract> createSession(@Nonnull RestEndpointExchange exchange) {
final Map<String, Object> parameters = getParametersFromRequest(exchange);
final String catalogName = (String) parameters.get(CatalogsHeaderDescriptor.NAME.name());
final CatalogContract catalog = restApiHandlingContext.getEvita().getCatalogInstance(catalogName)
final CatalogContract catalog = restHandlingContext.getEvita().getCatalogInstance(catalogName)
.orElseThrow(() -> new RestInternalError("Catalog `" + catalogName + "` does not exist."));

return Optional.of(restApiHandlingContext.getEvita().createReadOnlySession(catalog.getName()));
return Optional.of(restHandlingContext.getEvita().createReadOnlySession(catalog.getName()));
}

@Nonnull
Expand Down Expand Up @@ -139,34 +139,33 @@ protected Object convertResultIntoSerializableObject(@Nonnull RestEndpointExchan
//noinspection unchecked
final EvitaResponse<EntityClassifier> evitaResponse = (EvitaResponse<EntityClassifier>) result;
final QueryResponseBuilder queryResponseBuilder = QueryResponse.builder()
.recordPage(serializeRecordPage(evitaResponse));
.recordPage(serializeRecordPage(exchange, evitaResponse));
if (!evitaResponse.getExtraResults().isEmpty()) {
queryResponseBuilder
.extraResults(
extraResultsJsonSerializer.serialize(
evitaResponse.getExtraResults(),
exchange.session()
.getEntitySchema(evitaResponse.getSourceQuery().getCollection().getEntityType())
.orElseThrow(() -> new RestInternalError("No entity schema found for entity type `" + evitaResponse.getSourceQuery().getCollection().getEntityType() + "`."))
exchange.session().getEntitySchemaOrThrow(evitaResponse.getSourceQuery().getCollection().getEntityType()),
exchange.session().getCatalogSchema()
)
);
}
return queryResponseBuilder.build();
}

@Nonnull
private DataChunkDto serializeRecordPage(@Nonnull EvitaResponse<EntityClassifier> response) {
private DataChunkDto serializeRecordPage(@Nonnull RestEndpointExchange exchange, @Nonnull EvitaResponse<EntityClassifier> response) {
final DataChunk<EntityClassifier> recordPage = response.getRecordPage();

if (recordPage instanceof PaginatedList<EntityClassifier> paginatedList) {
return new PaginatedListDto(
paginatedList,
entityJsonSerializer.serialize(paginatedList.getData())
entityJsonSerializer.serialize(paginatedList.getData(), exchange.session().getCatalogSchema())
);
} else if (recordPage instanceof StripList<EntityClassifier> stripList) {
return new StripListDto(
stripList,
entityJsonSerializer.serialize(stripList.getData())
entityJsonSerializer.serialize(stripList.getData(), exchange.session().getCatalogSchema())
);
} else {
throw new RestInternalError("Unsupported data chunk type `" + recordPage.getClass().getName() + "`.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@

package io.evitadb.externalApi.lab.api.resolver.serializer;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.evitadb.api.requestResponse.data.EntityContract;
import io.evitadb.api.requestResponse.data.ReferenceContract;
import io.evitadb.api.requestResponse.schema.CatalogSchemaContract;
import io.evitadb.api.requestResponse.schema.EntitySchemaContract;
import io.evitadb.externalApi.lab.api.model.entity.GenericEntityDescriptor;
import io.evitadb.externalApi.rest.api.catalog.dataApi.resolver.serializer.EntityJsonSerializer;
import io.evitadb.externalApi.rest.io.RestHandlingContext;
Expand All @@ -43,20 +46,23 @@
@Slf4j
public class GenericEntityJsonSerializer extends EntityJsonSerializer {

public GenericEntityJsonSerializer(@Nonnull RestHandlingContext restHandlingContext) {
super(restHandlingContext);
public GenericEntityJsonSerializer(@Nonnull ObjectMapper objectMapper) {
super(false, objectMapper);
}

@Override
protected void serializeReferences(@Nonnull ObjectNode rootNode, @Nonnull EntityContract entity) {
protected void serializeReferences(@Nonnull ObjectNode rootNode,
@Nonnull EntityContract entity,
@Nonnull CatalogSchemaContract catalogSchema,
@Nonnull EntitySchemaContract entitySchema) {
if (entity.referencesAvailable() && !entity.getReferences().isEmpty()) {
final ObjectNode referencesObject = objectJsonSerializer.objectNode();

entity.getReferences()
.stream()
.map(ReferenceContract::getReferenceName)
.collect(Collectors.toCollection(TreeSet::new))
.forEach(it -> serializeReferencesWithSameName(referencesObject, entity, it));
.forEach(it -> serializeReferencesWithSameName(referencesObject, entity, it, catalogSchema, entitySchema));

rootNode.putIfAbsent(GenericEntityDescriptor.REFERENCES.name(), referencesObject);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public class GuiHandler extends ResourceHandler {
private static final String EVITALAB_READONLY_COOKIE = "evitalab_readonly";
private static final String EVITALAB_PRECONFIGURED_CONNECTIONS_COOKIE = "evitalab_pconnections";

private static final Pattern ASSETS_PATTERN = Pattern.compile("/assets/[a-zA-Z0-9\\-]+\\.[a-z0-9]+");
private static final Pattern ROOT_ASSETS_PATTERN = Pattern.compile("/[a-zA-Z0-9\\-]+\\.[a-z0-9]+");
private static final Pattern ASSETS_PATTERN = Pattern.compile("/assets/([a-zA-Z0-9\\-]+/)*[a-zA-Z0-9\\-]+\\.[a-z0-9]+");
private static final Pattern ROOT_ASSETS_PATTERN = Pattern.compile("(/logo)?/[a-zA-Z0-9\\-]+\\.[a-z0-9]+");

@Nonnull private final LabConfig labConfig;
@Nonnull private final String serverName;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5542a88

Please sign in to comment.