-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(service): create validate api metadata domain service
- Loading branch information
Showing
6 changed files
with
372 additions
and
0 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
.../main/java/io/gravitee/apim/core/api/domain_service/ValidateApiMetadataDomainService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright © 2015 The Gravitee team (http://gravitee.io) | ||
* | ||
* Licensed 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. | ||
*/ | ||
package io.gravitee.apim.core.api.domain_service; | ||
|
||
import io.gravitee.apim.core.DomainService; | ||
import io.gravitee.apim.core.api.exception.DuplicateApiMetadataKeyException; | ||
import io.gravitee.apim.core.api.exception.DuplicateApiMetadataNameException; | ||
import io.gravitee.apim.core.api.exception.InvalidApiMetadataValueException; | ||
import io.gravitee.apim.core.api.model.Api; | ||
import io.gravitee.apim.core.api.query_service.ApiMetadataQueryService; | ||
import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerDomainService; | ||
import io.gravitee.apim.core.metadata.crud_service.MetadataCrudService; | ||
import io.gravitee.apim.core.metadata.model.Metadata; | ||
import io.gravitee.apim.core.metadata.model.MetadataId; | ||
import jakarta.mail.internet.InternetAddress; | ||
import java.net.URL; | ||
import java.text.SimpleDateFormat; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import lombok.AllArgsConstructor; | ||
|
||
@AllArgsConstructor | ||
@DomainService | ||
public class ValidateApiMetadataDomainService { | ||
|
||
private final ApiMetadataQueryService metadataQueryService; | ||
private final MetadataCrudService metadataCrudService; | ||
private final ApiPrimaryOwnerDomainService apiPrimaryOwnerDomainService; | ||
private final ApiMetadataDecoderDomainService apiMetadataDecoderDomainService; | ||
|
||
private final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); | ||
|
||
public void validateUniqueKey(String apiId, String key) { | ||
this.metadataCrudService.findById( | ||
MetadataId.builder().key(key).referenceId(apiId).referenceType(Metadata.ReferenceType.API).build() | ||
) | ||
.ifPresent(m -> { | ||
throw new DuplicateApiMetadataKeyException(apiId, key); | ||
}); | ||
} | ||
|
||
public void validateUniqueName(String apiId, String name) { | ||
this.metadataQueryService.findApiMetadata(apiId) | ||
.values() | ||
.forEach(val -> { | ||
if (val.getName().equalsIgnoreCase(name)) { | ||
throw new DuplicateApiMetadataNameException(apiId, name); | ||
} | ||
}); | ||
} | ||
|
||
public void validateValueByFormat(Api api, String organizationId, String value, Metadata.MetadataFormat format) { | ||
var valueToCheck = Objects.nonNull(value) && value.startsWith("${") ? this.getDecodedValue(api, organizationId, value) : value; | ||
|
||
try { | ||
switch (format) { | ||
case URL: | ||
new URL(valueToCheck); | ||
break; | ||
case MAIL: | ||
final InternetAddress email = new InternetAddress(valueToCheck); | ||
email.validate(); | ||
break; | ||
case DATE: | ||
SIMPLE_DATE_FORMAT.setLenient(false); | ||
SIMPLE_DATE_FORMAT.parse(valueToCheck); | ||
break; | ||
case NUMERIC: | ||
Double.valueOf(valueToCheck); | ||
break; | ||
} | ||
} catch (Exception e) { | ||
throw new InvalidApiMetadataValueException(value, format.name()); | ||
} | ||
} | ||
|
||
private String getDecodedValue(Api api, String organizationId, String value) { | ||
var apiTemplate = new ApiMetadataDecoderDomainService.ApiMetadataDecodeContext( | ||
api, | ||
Map.of(), // Metadata cannot reference another metadata entry | ||
apiPrimaryOwnerDomainService.getApiPrimaryOwner(organizationId, api.getId()) | ||
); | ||
|
||
return this.apiMetadataDecoderDomainService.decodeMetadataValue(value, apiTemplate); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...e/src/main/java/io/gravitee/apim/core/api/exception/DuplicateApiMetadataKeyException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright © 2015 The Gravitee team (http://gravitee.io) | ||
* | ||
* Licensed 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. | ||
*/ | ||
package io.gravitee.apim.core.api.exception; | ||
|
||
import io.gravitee.apim.core.exception.ValidationDomainException; | ||
|
||
public class DuplicateApiMetadataKeyException extends ValidationDomainException { | ||
|
||
public DuplicateApiMetadataKeyException(String apiId, String key) { | ||
super("Key [" + key + "] already exists for API [" + apiId + "]"); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
.../src/main/java/io/gravitee/apim/core/api/exception/DuplicateApiMetadataNameException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright © 2015 The Gravitee team (http://gravitee.io) | ||
* | ||
* Licensed 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. | ||
*/ | ||
package io.gravitee.apim.core.api.exception; | ||
|
||
import io.gravitee.apim.core.exception.ValidationDomainException; | ||
|
||
public class DuplicateApiMetadataNameException extends ValidationDomainException { | ||
|
||
public DuplicateApiMetadataNameException(String apiId, String name) { | ||
super("Name [" + name + "] already exists for API [" + apiId + "]"); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...e/src/main/java/io/gravitee/apim/core/api/exception/InvalidApiMetadataValueException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright © 2015 The Gravitee team (http://gravitee.io) | ||
* | ||
* Licensed 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. | ||
*/ | ||
package io.gravitee.apim.core.api.exception; | ||
|
||
import io.gravitee.apim.core.exception.ValidationDomainException; | ||
|
||
public class InvalidApiMetadataValueException extends ValidationDomainException { | ||
|
||
public InvalidApiMetadataValueException(String value, String format) { | ||
super("Invalid value [" + value + "] for format [" + format + "]"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 193 additions & 0 deletions
193
...t/java/io/gravitee/apim/core/api/domain_service/ValidateApiMetadataDomainServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/* | ||
* Copyright © 2015 The Gravitee team (http://gravitee.io) | ||
* | ||
* Licensed 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. | ||
*/ | ||
package io.gravitee.apim.core.api.domain_service; | ||
|
||
import static org.assertj.core.api.Assertions.*; | ||
|
||
import inmemory.*; | ||
import io.gravitee.apim.core.api.exception.InvalidApiMetadataValueException; | ||
import io.gravitee.apim.core.api.model.Api; | ||
import io.gravitee.apim.core.audit.domain_service.AuditDomainService; | ||
import io.gravitee.apim.core.membership.domain_service.ApiPrimaryOwnerDomainService; | ||
import io.gravitee.apim.core.membership.model.Membership; | ||
import io.gravitee.apim.core.membership.model.Role; | ||
import io.gravitee.apim.core.metadata.model.Metadata; | ||
import io.gravitee.apim.core.user.model.BaseUserEntity; | ||
import io.gravitee.apim.infra.json.jackson.JacksonJsonDiffProcessor; | ||
import io.gravitee.apim.infra.template.FreemarkerTemplateProcessor; | ||
import java.util.List; | ||
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.*; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
class ValidateApiMetadataDomainServiceTest { | ||
|
||
private static final String API_ID = "my-api"; | ||
private static final String ORGANIZATION_ID = "organization-id"; | ||
AuditCrudServiceInMemory auditCrudService = new AuditCrudServiceInMemory(); | ||
UserCrudServiceInMemory userCrudService = new UserCrudServiceInMemory(); | ||
GroupQueryServiceInMemory groupQueryService = new GroupQueryServiceInMemory(); | ||
MembershipCrudServiceInMemory membershipCrudService = new MembershipCrudServiceInMemory(); | ||
MembershipQueryServiceInMemory membershipQueryService = new MembershipQueryServiceInMemory(membershipCrudService); | ||
RoleQueryServiceInMemory roleQueryService = new RoleQueryServiceInMemory(); | ||
MetadataCrudServiceInMemory metadataCrudService = new MetadataCrudServiceInMemory(); | ||
ApiMetadataQueryServiceInMemory apiMetadataQueryService; | ||
ValidateApiMetadataDomainService service; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
var auditDomainService = new AuditDomainService(auditCrudService, userCrudService, new JacksonJsonDiffProcessor()); | ||
var apiMetadataDecoderDomainService = new ApiMetadataDecoderDomainService( | ||
apiMetadataQueryService, | ||
new FreemarkerTemplateProcessor() | ||
); | ||
service = | ||
new ValidateApiMetadataDomainService( | ||
apiMetadataQueryService, | ||
metadataCrudService, | ||
new ApiPrimaryOwnerDomainService( | ||
auditDomainService, | ||
groupQueryService, | ||
membershipCrudService, | ||
membershipQueryService, | ||
roleQueryService, | ||
userCrudService | ||
), | ||
apiMetadataDecoderDomainService | ||
); | ||
|
||
roleQueryService.initWith( | ||
List.of( | ||
Role | ||
.builder() | ||
.id("role-id") | ||
.scope(Role.Scope.API) | ||
.referenceType(Role.ReferenceType.ORGANIZATION) | ||
.referenceId(ORGANIZATION_ID) | ||
.name("PRIMARY_OWNER") | ||
.build() | ||
) | ||
); | ||
membershipQueryService.initWith( | ||
List.of( | ||
Membership | ||
.builder() | ||
.id("member-id") | ||
.memberId("my-member-id") | ||
.memberType(Membership.Type.USER) | ||
.referenceType(Membership.ReferenceType.API) | ||
.referenceId(API_ID) | ||
.roleId("role-id") | ||
.build() | ||
) | ||
); | ||
userCrudService.initWith(List.of(BaseUserEntity.builder().id("my-member-id").email("one_valid@email.com").build())); | ||
|
||
metadataCrudService.initWith( | ||
List.of( | ||
Metadata.builder().referenceId(API_ID).referenceType(Metadata.ReferenceType.API).key("exists").value("john@doe.com").build() | ||
) | ||
); | ||
} | ||
|
||
@AfterEach | ||
void tearDown() { | ||
Stream | ||
.of( | ||
auditCrudService, | ||
membershipCrudService, | ||
roleQueryService, | ||
userCrudService, | ||
metadataCrudService, | ||
groupQueryService, | ||
membershipQueryService | ||
) | ||
.forEach(InMemoryAlternative::reset); | ||
} | ||
|
||
@Nested | ||
class ValidateValueByFormat { | ||
|
||
@ParameterizedTest | ||
@MethodSource("provideValidParameters") | ||
void should_validate_value(final String value, final Metadata.MetadataFormat format) { | ||
assertThatCode(() -> service.validateValueByFormat(Api.builder().build(), ORGANIZATION_ID, value, format)) | ||
.doesNotThrowAnyException(); | ||
} | ||
|
||
public static Stream<Arguments> provideValidParameters() { | ||
return Stream.of( | ||
Arguments.of("john@doe.com", Metadata.MetadataFormat.MAIL), | ||
Arguments.of("https://my-url.com", Metadata.MetadataFormat.URL), | ||
Arguments.of("https://my-url", Metadata.MetadataFormat.URL), | ||
Arguments.of("2005-01-01", Metadata.MetadataFormat.DATE), | ||
Arguments.of("2021-10-06T15:17:15.282+00:00", Metadata.MetadataFormat.DATE), | ||
Arguments.of("42", Metadata.MetadataFormat.NUMERIC), | ||
Arguments.of("100.5", Metadata.MetadataFormat.NUMERIC), | ||
Arguments.of("true", Metadata.MetadataFormat.BOOLEAN), | ||
Arguments.of("false", Metadata.MetadataFormat.BOOLEAN), | ||
Arguments.of("any value for boolean works", Metadata.MetadataFormat.BOOLEAN), | ||
Arguments.of("100", Metadata.MetadataFormat.STRING) | ||
); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("provideInvalidParameters") | ||
void should_throw_error_on_invalid_value(final String value, final Metadata.MetadataFormat format) { | ||
assertThatThrownBy(() -> service.validateValueByFormat(Api.builder().id(API_ID).build(), ORGANIZATION_ID, value, format)) | ||
.isInstanceOf(InvalidApiMetadataValueException.class); | ||
} | ||
|
||
public static Stream<Arguments> provideInvalidParameters() { | ||
return Stream.of( | ||
Arguments.of("johndoe.com", Metadata.MetadataFormat.MAIL), | ||
Arguments.of("@johndoe.com", Metadata.MetadataFormat.MAIL), | ||
Arguments.of("my-url", Metadata.MetadataFormat.URL), | ||
Arguments.of("my-url.com", Metadata.MetadataFormat.URL), | ||
Arguments.of("2005", Metadata.MetadataFormat.DATE), | ||
Arguments.of("1/2", Metadata.MetadataFormat.NUMERIC) | ||
); | ||
} | ||
} | ||
|
||
@Test | ||
void should_throw_error_when_decoding_mail_value_referencing_another_metadata_key() { | ||
assertThatThrownBy(() -> | ||
service.validateValueByFormat( | ||
Api.builder().id(API_ID).build(), | ||
ORGANIZATION_ID, | ||
"${api.metadata['exists']}", | ||
Metadata.MetadataFormat.MAIL | ||
) | ||
) | ||
.isInstanceOf(InvalidApiMetadataValueException.class); | ||
} | ||
|
||
@Test | ||
void should_not_throw_error_when_decoding_string_value_referencing_another_metadata_key() { | ||
assertThatCode(() -> | ||
service.validateValueByFormat( | ||
Api.builder().id(API_ID).build(), | ||
ORGANIZATION_ID, | ||
"${api.metadata['exists']}", | ||
Metadata.MetadataFormat.STRING | ||
) | ||
) | ||
.doesNotThrowAnyException(); | ||
} | ||
} |