Skip to content

Commit

Permalink
Add more validators. Return UNPROCESSABLE_ENTITY when validation fails
Browse files Browse the repository at this point in the history
  • Loading branch information
sake92 committed Dec 22, 2024
1 parent f506257 commit 388f40d
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .mill-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.11.12
0.12.4
2 changes: 1 addition & 1 deletion DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ scala-cli compile examples\scala-cli
git diff
git commit -am "msg"

$VERSION="0.7.4"
$VERSION="0.7.5"
git commit --allow-empty -m "Release $VERSION"
git tag -a $VERSION -m "Release $VERSION"
git push --atomic origin main $VERSION
Expand Down
2 changes: 1 addition & 1 deletion examples/api/test/src/JsonApiSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class JsonApiSuite extends munit.FunSuite {
}
val resProblem = ex.response.text().parseJson[ProblemDetails]

assertEquals(ex.response.statusCode, 400)
assertEquals(ex.response.statusCode, 422)
println(resProblem.invalidArguments)
assert(
resProblem.invalidArguments.contains(
Expand Down
123 changes: 88 additions & 35 deletions mill
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# You can give the required mill version with --mill-version parameter
# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
#
# Project page: https://github.com/lefou/millw
# Script Version: 0.4.6
# Original Project page: https://github.com/lefou/millw
# Script Version: 0.4.12
#
# If you want to improve this script, please also contribute your changes back!
#
Expand All @@ -14,7 +14,12 @@
set -e

if [ -z "${DEFAULT_MILL_VERSION}" ] ; then
DEFAULT_MILL_VERSION="0.10.10"
DEFAULT_MILL_VERSION="0.11.4"
fi


if [ -z "${GITHUB_RELEASE_CDN}" ] ; then
GITHUB_RELEASE_CDN=""
fi


Expand Down Expand Up @@ -44,16 +49,16 @@ fi
# If not already set, read .mill-version file
if [ -z "${MILL_VERSION}" ] ; then
if [ -f ".mill-version" ] ; then
MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)"
MILL_VERSION="$(tr '\r' '\n' < .mill-version | head -n 1 2> /dev/null)"
elif [ -f ".config/mill-version" ] ; then
MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)"
MILL_VERSION="$(tr '\r' '\n' < .config/mill-version | head -n 1 2> /dev/null)"
fi
fi

if [ -n "${XDG_CACHE_HOME}" ] ; then
MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download"
else
MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download"
MILL_USER_CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/mill"

if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then
MILL_DOWNLOAD_PATH="${MILL_USER_CACHE_DIR}/download"
fi

# If not already set, try to fetch newest from Github
Expand Down Expand Up @@ -99,34 +104,71 @@ fi
MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}"

try_to_use_system_mill() {
if [ "$(uname)" != "Linux" ]; then
return 0
fi

MILL_IN_PATH="$(command -v mill || true)"

if [ -z "${MILL_IN_PATH}" ]; then
return
return 0
fi

SYSTEM_MILL_FIRST_TWO_BYTES=$(head --bytes=2 "${MILL_IN_PATH}")
if [ "${SYSTEM_MILL_FIRST_TWO_BYTES}" = "#!" ]; then
# MILL_IN_PATH is (very likely) a shell script and not the mill
# executable, ignore it.
return 0
fi

UNIVERSAL_SCRIPT_MAGIC="@ 2>/dev/null # 2>nul & echo off & goto BOF"
SYSTEM_MILL_PATH=$(readlink -e "${MILL_IN_PATH}")
SYSTEM_MILL_SIZE=$(stat --format=%s "${SYSTEM_MILL_PATH}")
SYSTEM_MILL_MTIME=$(stat --format=%y "${SYSTEM_MILL_PATH}")

if ! head -c 128 "${MILL_IN_PATH}" | grep -qF "${UNIVERSAL_SCRIPT_MAGIC}"; then
if [ -n "${MILLW_VERBOSE}" ]; then
echo "Could not determine mill version of ${MILL_IN_PATH}, as it does not start with the universal script magic2" 1>&2
if [ ! -d "${MILL_USER_CACHE_DIR}" ]; then
mkdir -p "${MILL_USER_CACHE_DIR}"
fi

SYSTEM_MILL_INFO_FILE="${MILL_USER_CACHE_DIR}/system-mill-info"
if [ -f "${SYSTEM_MILL_INFO_FILE}" ]; then
parseSystemMillInfo() {
LINE_NUMBER="${1}"
# Select the line number of the SYSTEM_MILL_INFO_FILE, cut the
# variable definition in that line in two halves and return
# the value, and finally remove the quotes.
sed -n "${LINE_NUMBER}p" "${SYSTEM_MILL_INFO_FILE}" |\
cut -d= -f2 |\
sed 's/"\(.*\)"/\1/'
}

CACHED_SYSTEM_MILL_PATH=$(parseSystemMillInfo 1)
CACHED_SYSTEM_MILL_VERSION=$(parseSystemMillInfo 2)
CACHED_SYSTEM_MILL_SIZE=$(parseSystemMillInfo 3)
CACHED_SYSTEM_MILL_MTIME=$(parseSystemMillInfo 4)

if [ "${SYSTEM_MILL_PATH}" = "${CACHED_SYSTEM_MILL_PATH}" ] \
&& [ "${SYSTEM_MILL_SIZE}" = "${CACHED_SYSTEM_MILL_SIZE}" ] \
&& [ "${SYSTEM_MILL_MTIME}" = "${CACHED_SYSTEM_MILL_MTIME}" ]; then
if [ "${CACHED_SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then
MILL="${SYSTEM_MILL_PATH}"
return 0
else
return 0
fi
fi
return
fi

# Roughly the size of the universal script.
MILL_VERSION_SEARCH_RANGE="2403"
MILL_IN_PATH_VERSION=$(head -c "${MILL_VERSION_SEARCH_RANGE}" "${MILL_IN_PATH}" |\
sed -n 's/^.*-DMILL_VERSION=\([^\s]*\) .*$/\1/p' |\
head -n 1)
SYSTEM_MILL_VERSION=$(${SYSTEM_MILL_PATH} --version | head -n1 | sed -n 's/^Mill.*version \(.*\)/\1/p')

if [ -z "${MILL_IN_PATH_VERSION}" ]; then
echo "Could not determine mill version, even though ${MILL_IN_PATH} has the universal script magic" 1>&2
return
fi
cat <<EOF > "${SYSTEM_MILL_INFO_FILE}"
CACHED_SYSTEM_MILL_PATH="${SYSTEM_MILL_PATH}"
CACHED_SYSTEM_MILL_VERSION="${SYSTEM_MILL_VERSION}"
CACHED_SYSTEM_MILL_SIZE="${SYSTEM_MILL_SIZE}"
CACHED_SYSTEM_MILL_MTIME="${SYSTEM_MILL_MTIME}"
EOF

if [ "${MILL_IN_PATH_VERSION}" = "${MILL_VERSION}" ]; then
MILL="${MILL_IN_PATH}"
if [ "${SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then
MILL="${SYSTEM_MILL_PATH}"
fi
}
try_to_use_system_mill
Expand All @@ -140,22 +182,34 @@ if [ ! -s "${MILL}" ] ; then
if [ -x "${OLD_MILL}" ] ; then
MILL="${OLD_MILL}"
else
VERSION_PREFIX="$(echo $MILL_VERSION | cut -b -4)"
case $VERSION_PREFIX in
0.0. | 0.1. | 0.2. | 0.3. | 0.4. )
case $MILL_VERSION in
0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* )
DOWNLOAD_SUFFIX=""
DOWNLOAD_FROM_MAVEN=0
;;
0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* )
DOWNLOAD_SUFFIX="-assembly"
DOWNLOAD_FROM_MAVEN=0
;;
*)
DOWNLOAD_SUFFIX="-assembly"
DOWNLOAD_FROM_MAVEN=1
;;
esac
unset VERSION_PREFIX

DOWNLOAD_FILE=$(mktemp mill.XXXXXX)

if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then
DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/${MILL_VERSION}/mill-dist-${MILL_VERSION}.jar"
else
MILL_VERSION_TAG=$(echo "$MILL_VERSION" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}"
unset MILL_VERSION_TAG
fi

# TODO: handle command not found
echo "Downloading mill ${MILL_VERSION} from ${MILL_REPO_URL}/releases ..." 1>&2
MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}"
echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2
${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}"
chmod +x "${DOWNLOAD_FILE}"
mkdir -p "${MILL_DOWNLOAD_PATH}"
mv "${DOWNLOAD_FILE}" "${MILL}"
Expand All @@ -180,9 +234,8 @@ unset MILL_DOWNLOAD_PATH
unset MILL_OLD_DOWNLOAD_PATH
unset OLD_MILL
unset MILL_VERSION
unset MILL_VERSION_TAG
unset MILL_REPO_URL

# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes
# shellcheck disable=SC2086
exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@"
exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@"
77 changes: 62 additions & 15 deletions mill.bat
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ rem This is a wrapper script, that automatically download mill from GitHub relea
rem You can give the required mill version with --mill-version parameter
rem If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
rem
rem Project page: https://github.com/lefou/millw
rem Script Version: 0.4.6
rem Original Project page: https://github.com/lefou/millw
rem Script Version: 0.4.12
rem
rem If you want to improve this script, please also contribute your changes back!
rem
Expand All @@ -16,13 +16,17 @@ rem but I don't think we need to support them in 2019
setlocal enabledelayedexpansion

if [!DEFAULT_MILL_VERSION!]==[] (
set "DEFAULT_MILL_VERSION=0.10.10"
set "DEFAULT_MILL_VERSION=0.11.4"
)

if [!GITHUB_RELEASE_CDN!]==[] (
set "GITHUB_RELEASE_CDN="
)

if [!MILL_MAIN_CLI!]==[] (
set "MILL_MAIN_CLI=%~f0"
)

set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill"

rem %~1% removes surrounding quotes
Expand Down Expand Up @@ -59,19 +63,62 @@ if [!MILL_VERSION!]==[] (
set MILL_VERSION=%DEFAULT_MILL_VERSION%
)

set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download
if [!MILL_DOWNLOAD_PATH!]==[] (
set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download
)

rem without bat file extension, cmd doesn't seem to be able to run it
set MILL=%MILL_DOWNLOAD_PATH%\!MILL_VERSION!.bat

if not exist "%MILL%" (
set VERSION_PREFIX=%MILL_VERSION:~0,4%
rem Since 0.5.0
set DOWNLOAD_SUFFIX=-assembly
if [!VERSION_PREFIX!]==[0.0.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.1.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.2.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.3.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.4.] set DOWNLOAD_SUFFIX=
rem Since 0.11.0
set DOWNLOAD_FROM_MAVEN=1
if [!VERSION_PREFIX!]==[0.0.] (
set DOWNLOAD_SUFFIX=
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.1.] (
set DOWNLOAD_SUFFIX=
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.2.] (
set DOWNLOAD_SUFFIX=
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.3.] (
set DOWNLOAD_SUFFIX=
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.4.] (
set DOWNLOAD_SUFFIX=
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.5.] (
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.6.] (
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.7.] (
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.8.] (
set DOWNLOAD_FROM_MAVEN=0
)
if [!VERSION_PREFIX!]==[0.9.] (
set DOWNLOAD_FROM_MAVEN=0
)
set VERSION_PREFIX=%MILL_VERSION:~0,5%
if [!VERSION_PREFIX!]==[0.10.] (
set DOWNLOAD_FROM_MAVEN=0
)
set VERSION_PREFIX=%MILL_VERSION:~0,8%
if [!VERSION_PREFIX!]==[0.11.0-M] (
set DOWNLOAD_FROM_MAVEN=0
)
set VERSION_PREFIX=

for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A
Expand All @@ -86,7 +133,11 @@ if not exist "%MILL%" (
rem there seems to be no way to generate a unique temporary file path (on native Windows)
set DOWNLOAD_FILE=%MILL%.tmp

set DOWNLOAD_URL=!GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!DOWNLOAD_SUFFIX!
if [!DOWNLOAD_FROM_MAVEN!]==[1] (
set DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/!MILL_VERSION!/mill-dist-!MILL_VERSION!.jar
) else (
set DOWNLOAD_URL=!GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!DOWNLOAD_SUFFIX!
)

echo Downloading mill %MILL_VERSION% from !DOWNLOAD_URL! ... 1>&2

Expand Down Expand Up @@ -117,10 +168,6 @@ set MILL_DOWNLOAD_PATH=
set MILL_VERSION=
set MILL_REPO_URL=

if [!MILL_MAIN_CLI!]==[] (
set "MILL_MAIN_CLI=%0"
)

rem Need to preserve the first position of those listed options
set MILL_FIRST_ARG=
if [%~1%]==[--bsp] (
Expand Down Expand Up @@ -170,4 +217,4 @@ if not [!MILL_FIRST_ARG!]==[] (
)
)

"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS%
"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS%
4 changes: 2 additions & 2 deletions sharaf/src/ba/sake/sharaf/exceptions/ExceptionMapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object ExceptionMapper {
cause match
case e: validson.ValidsonException =>
val fieldValidationErrors = e.errors.mkString("[", "; ", "]")
Response.withBody(s"Validation errors: $fieldValidationErrors").withStatus(StatusCodes.BAD_REQUEST)
Response.withBody(s"Validation errors: $fieldValidationErrors").withStatus(StatusCodes.UNPROCESSABLE_ENTITY)
case e: querson.ParsingException =>
Response.withBody(e.getMessage()).withStatus(StatusCodes.BAD_REQUEST)
case e: tupson.ParsingException =>
Expand Down Expand Up @@ -63,7 +63,7 @@ object ExceptionMapper {
e.errors.map(err => ArgumentProblem(err.path, err.msg, Some(err.value.toString)))
val problemDetails =
ProblemDetails(StatusCodes.BAD_REQUEST, "Validation errors", invalidArguments = fieldValidationErrors)
Response.withBody(problemDetails).withStatus(StatusCodes.BAD_REQUEST)
Response.withBody(problemDetails).withStatus(StatusCodes.UNPROCESSABLE_ENTITY)
case e: querson.ParsingException =>
val parsingErrors = e.errors.map(err => ArgumentProblem(err.path, err.msg, err.value.map(_.toString)))
val problemDetails =
Expand Down
25 changes: 24 additions & 1 deletion validson/src/ba/sake/validson/Validator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,36 @@ trait Validator[T] {
def minLength(getter: T => sourcecode.Text[String], value: Long): Validator[T] =
validatorImpl(getter, _.length >= value, s"must be >= $value")

def maxLength(getter: T => sourcecode.Text[String], value: Long): Validator[T] =
validatorImpl(getter, _.length <= value, s"must be <= $value")

def contains(getter: T => sourcecode.Text[String], value: String): Validator[T] =
validatorImpl(getter, _.contains(value), s"must contain $value")

def matches(getter: T => sourcecode.Text[String], value: String): Validator[T] =
validatorImpl(getter, _.matches(value), s"must contain $value")

// seqs
def notEmptySeq(getter: T => sourcecode.Text[Seq[?]]): Validator[T] =
def notEmpty(getter: T => sourcecode.Text[Seq[?]]): Validator[T] =
validatorImpl(getter, !_.isEmpty, "must not be empty")

def minItems(getter: T => sourcecode.Text[Seq[?]], value: String): Validator[T] =
validatorImpl(getter, _.size >= value, s"must be >= $value")

def maxItems(getter: T => sourcecode.Text[Seq[?]], value: String): Validator[T] =
validatorImpl(getter, _.size <= value, s"must be <= $value")

// sets
def notEmpty(getter: T => sourcecode.Text[Set[?]]): Validator[T] =
validatorImpl(getter, !_.isEmpty, "must not be empty")

def minItems(getter: T => sourcecode.Text[Set[?]], value: String): Validator[T] =
validatorImpl(getter, _.size >= value, s"must be >= $value")

def maxItems(getter: T => sourcecode.Text[Set[?]], value: String): Validator[T] =
validatorImpl(getter, _.size <= value, s"must be <= $value")


private def validatorImpl[F](getter: T => sourcecode.Text[F], predicate: F => Boolean, msg: String): Validator[T] =
(value: T) => {
val fieldText = getter(value)
Expand Down

0 comments on commit 388f40d

Please sign in to comment.