diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 8ab3fe15..c13eb62a 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("io.micronaut.application") version "3.5.1" } -version = "1.0" +version = "2.0" group = "club.devcord.devmarkt" repositories { @@ -32,7 +32,7 @@ dependencies { implementation("io.micronaut.data:micronaut-data-jdbc") implementation("io.micronaut.flyway:micronaut-flyway") implementation("io.micronaut.graphql:micronaut-graphql") - implementation("com.graphql-java-kickstart:graphql-java-tools:13.0.0") + implementation("com.graphql-java-kickstart:graphql-java-tools:12.1.0") implementation("com.graphql-java:graphql-java-extended-validation:18.1-hibernate-validator-6.2.0.Final") implementation("io.micronaut:micronaut-validation") runtimeOnly("org.postgresql:postgresql") diff --git a/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/ErrorCode.java b/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/ErrorCode.java index b080951a..dbd46e08 100644 --- a/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/ErrorCode.java +++ b/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/ErrorCode.java @@ -26,5 +26,6 @@ public enum ErrorCode implements club.devcord.devmarkt.responses.failure.ErrorCo NO_QUESTION, ANSWER_TOO_SHORT, ALREADY_ACCEPTED, - QUESTION_UNANSWERED + QUESTION_UNANSWERED, + TOO_LARGE } diff --git a/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/TooLargeErrorData.java b/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/TooLargeErrorData.java new file mode 100644 index 00000000..12f45c8e --- /dev/null +++ b/backend/src/main/java/club/devcord/devmarkt/responses/failure/application/TooLargeErrorData.java @@ -0,0 +1,28 @@ +/* + * Copyright 2022 Contributors to the Devmarkt-Backend project + * + * 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 club.devcord.devmarkt.responses.failure.application; + +import club.devcord.devmarkt.entities.application.Application; +import club.devcord.devmarkt.graphql.GraphQLType; +import club.devcord.devmarkt.responses.failure.ErrorData; + +@GraphQLType +public record TooLargeErrorData( + int size +) implements ErrorData { + +} diff --git a/backend/src/main/java/club/devcord/devmarkt/services/ApplicationService.java b/backend/src/main/java/club/devcord/devmarkt/services/ApplicationService.java index 4d8a94a8..475657c0 100644 --- a/backend/src/main/java/club/devcord/devmarkt/services/ApplicationService.java +++ b/backend/src/main/java/club/devcord/devmarkt/services/ApplicationService.java @@ -31,6 +31,7 @@ import club.devcord.devmarkt.responses.failure.application.AnswerTooShortApplicationErrorData; import club.devcord.devmarkt.responses.failure.application.ErrorCode; import club.devcord.devmarkt.responses.failure.application.NumberApplicationErrorData; +import club.devcord.devmarkt.responses.failure.application.TooLargeErrorData; import jakarta.inject.Singleton; import java.util.ArrayList; import java.util.Collection; @@ -110,8 +111,9 @@ public Application createApplication(String templateName, ArrayList answ throw new FailureException(ErrorCode.TEMPLATE_NOT_FOUND); } - var errors = new ArrayList>(); var template = templateOpt.get(); + var errors = new ArrayList>(); + validateSize(template, answers, errors); validateAndPrepareAnswers(answers, template, answer -> null, null, errors, true); if (!errors.isEmpty()) { @@ -125,6 +127,21 @@ public Application createApplication(String templateName, ArrayList answ return saved; } + // according to the discord embed limits -> https://discordjs.guide/popular-topics/embeds.html#embed-limits + private void validateSize(Template template, List answers, ArrayList> errors) { + var size = template.name().length(); + for (var question : template.questions()) { + size += question.question().length(); + } + for (var answer : answers) { + size += answer.answer().length(); + } + + if (size > 5800) { // leave 200 chars for extra text + errors.add(new Error<>(ErrorCode.TOO_LARGE, new TooLargeErrorData(size))); + } + } + private void validateAndPrepareAnswers(ArrayList answers, Template template, Function numberFunc, Application application, Collection> errors, boolean checkUnansweredQuestions) { diff --git a/backend/src/main/resources/graphql/schemas/application.graphql b/backend/src/main/resources/graphql/schemas/application.graphql index 5c916c01..d0979ebc 100644 --- a/backend/src/main/resources/graphql/schemas/application.graphql +++ b/backend/src/main/resources/graphql/schemas/application.graphql @@ -1,6 +1,6 @@ input AnswerInput { number: NonNegativeInt! - answer: String! @NotBlank + answer: String! @NotBlank @Size(min: 1, max: 1024) } type Answer { @@ -25,6 +25,10 @@ type AnswerTooShortApplicationErrorData { number: NonNegativeInt! } +type TooLargeErrorData { + size: Int +} + union ApplicationResponse = Application | Failure type Application { diff --git a/backend/src/main/resources/graphql/schemas/common.graphql b/backend/src/main/resources/graphql/schemas/common.graphql index 05ec7804..7b64e211 100644 --- a/backend/src/main/resources/graphql/schemas/common.graphql +++ b/backend/src/main/resources/graphql/schemas/common.graphql @@ -19,7 +19,7 @@ type Query { type Mutation { createTemplate( name: TemplateName! - questions: [QuestionInput!]! + questions: [QuestionInput!]! @ContainerSize(min: 1, max: 25) ): TemplateResponse @Auth(role: "admin") deleteTemplate( diff --git a/backend/src/main/resources/graphql/schemas/failure.graphql b/backend/src/main/resources/graphql/schemas/failure.graphql index 2c6a2443..54b85a73 100644 --- a/backend/src/main/resources/graphql/schemas/failure.graphql +++ b/backend/src/main/resources/graphql/schemas/failure.graphql @@ -1,4 +1,4 @@ -union FailureData = NumberTemplateErrorData | NumberApplicationErrorData | AnswerTooShortApplicationErrorData +union FailureData = NumberTemplateErrorData | NumberApplicationErrorData | AnswerTooShortApplicationErrorData | TooLargeErrorData type Failure { errors: [Error!]! diff --git a/backend/src/main/resources/graphql/schemas/misc.graphql b/backend/src/main/resources/graphql/schemas/misc.graphql index 26a6fedb..ab88c511 100644 --- a/backend/src/main/resources/graphql/schemas/misc.graphql +++ b/backend/src/main/resources/graphql/schemas/misc.graphql @@ -10,6 +10,9 @@ on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION directive @Range(min: Int = 0, max: Int = 2147483647, message: String = "graphql.validation.Range.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION +directive @ContainerSize(min: Int = 0, max: Int = 2147483647, message: String = "graphql.validation.ContainerSize.message") +on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION + directive @Auth(role: String!) on FIELD_DEFINITION directive @AuthOwnApplication(idField: String!) on FIELD_DEFINITION # user must own application or be admin diff --git a/backend/src/main/resources/graphql/schemas/question.graphql b/backend/src/main/resources/graphql/schemas/question.graphql index 248fee0f..05155da6 100644 --- a/backend/src/main/resources/graphql/schemas/question.graphql +++ b/backend/src/main/resources/graphql/schemas/question.graphql @@ -1,5 +1,5 @@ input QuestionInput { - question: String! @NotBlank @Size(min: 1, max: 200) + question: String! @NotBlank @Size(min: 1, max: 256) number: NonNegativeInt! multiline: Boolean! minAnswerLength: Int! = 1 @Range(min: 1, max: 1024) diff --git a/qa/src/spec/template.spec.js b/qa/src/spec/template.spec.js index dd014f61..e9523d66 100644 --- a/qa/src/spec/template.spec.js +++ b/qa/src/spec/template.spec.js @@ -69,7 +69,7 @@ const tests = [ name: 'duplicated', query: 'template/create-template.graphql', response: 'template/create-duplicated.json', - variables: {name: 'Dev searched', questions: []}, + variables: templateCreateVars("Dev searched"), verify: { query: "template/templates.graphql", response: "template/templates.json"