
skinny.controller.SkinnyApiResourceActions.scala Maven / Gradle / Ivy
package skinny.controller
import skinny._
import skinny.ParamType
import skinny.controller.feature.SkinnyControllerCommonBase
import skinny.validator.{ NewValidation, MapValidator }
import skinny.exception.StrongParametersException
import java.util.Locale
/**
* Actions for Skinny API resource.
*/
trait SkinnyApiResourceActions[Id] { self: SkinnyControllerCommonBase =>
/**
* SkinnyModel for this resource.
*/
protected def model: SkinnyModel[Id, _]
/**
* Id field name.
*/
protected def idName: String = "id"
/**
* Id parameter name.
*/
protected def idParamName: String = toSnakeCase(idName)
/**
* Resource name in the plural. This name will be used for path and directory name to locale template files.
*/
protected def resourcesName: String
/**
* Resource name in the singular. This name will be used for path and validator's prefix.
*/
protected def resourceName: String
/**
* Root element name in the XML response.
*/
override protected def xmlRootName = resourcesName
/**
* Each resource item element name in the XML response.
*/
override protected def xmlItemName = resourceName
/**
* Creates validator with prefix(resourceName).
*
* @param params params
* @param validations validations
* @param locale current locale
* @return validator
*/
override def validation(params: Params, validations: NewValidation*)(
implicit
locale: Locale = currentLocale(context).orNull[Locale]
): MapValidator = {
validationWithPrefix(params, resourceName, validations: _*)(context)
}
/**
* Use relative path if true. This is set as false by default.
*
* If you set this as true, routing will become simpler but /{resources}.xml or /{resources}.json don't work.
*/
protected def useRelativePathForResourcesBasePath: Boolean = false
/**
* Base path prefix. (e.g. /admin/{resourcesName} )
*/
protected def resourcesBasePathPrefix: String = ""
/**
* Base path.
*/
protected def resourcesBasePath: String = {
resourcesBasePathPrefix + (if (useRelativePathForResourcesBasePath) "" else s"/${resourcesName}")
}
/**
* Normalized base path. This method should not be overridden.
*/
protected final def normalizedResourcesBasePath: String = {
resourcesBasePath.replaceFirst("^/", "").replaceFirst("/$", "")
}
/**
* Outputs debug logging for passed parameters.
*
* @param form input form
* @param id id if exists
*/
protected def debugLoggingParameters(form: MapValidator, id: Option[Id] = None) = {
val forId = id.map { id => s" for [id -> ${id}]" }.getOrElse("")
val params = form.paramMap.map { case (name, value) => s"${name} -> '${value}'" }.mkString("[", ", ", "]")
logger.debug(s"Parameters${forId}: ${params}")
}
/**
* Outputs debug logging for permitted parameters.
*
* @param parameters permitted strong parameters
* @param id id if exists
*/
protected def debugLoggingPermittedParameters(parameters: PermittedStrongParameters, id: Option[Id] = None) = {
val forId = id.map { id => s" for [id -> ${id}]" }.getOrElse("")
val params = parameters.params.map { case (name, (v, t)) => s"${name} -> '${v}' as ${t}" }.mkString("[", ", ", "]")
logger.debug(s"Permitted parameters${forId}: ${params}")
}
// ----------------------------
// Actions for this resource
// ----------------------------
protected def enablePagination: Boolean = true
protected def pageSize: Int = 20
protected def pageSizeParamName: String = "size"
protected def pageNoParamName: String = "page"
/**
* Shows a list of resource.
*
* GET /{resources}/
* GET /{resources}/?page=1&size=10
* GET /{resources}
* GET /{resources}.xml
* GET /{resources}.json
*
* @param format format
* @return list of resource
*/
def showResources()(implicit format: Format = Format.HTML): Any = withFormat(format) {
implicit val ctx = context
if (enablePagination) {
val pageSize: Int = params.getAs[Int](pageSizeParamName).getOrElse(this.pageSize)
val pageNo: Int = params.getAs[Int](pageNoParamName).getOrElse(1)
renderWithFormat(findResources(pageSize, pageNo))
} else {
renderWithFormat(findResources())
}
}
protected def countResources(): Long = model.countAllModels()
protected def findResources(pageSize: Int, pageNo: Int): List[_] = model.findModels(pageSize, pageNo)
protected def findResources(): List[_] = model.findAllModels()
/**
* Show single resource.
*
* GET /{resources}/{id}
* GET /{resources}/{id}.xml
* GET /{resources}/{id}.json
*
* @param id id
* @param format format
* @return single resource
*/
def showResource(id: Id)(implicit format: Format = Format.HTML): Any = withFormat(format) {
renderWithFormat(findResource(id).getOrElse(haltWithBody(404)(context)))
}
protected def findResource(id: Id): Option[_] = model.findModel(id)
/**
* Input form for creation
*/
protected def createForm: MapValidator
/**
* Params for creation.
*/
protected def createParams: Params = Params(params(context))
/**
* Strong parameter definitions for creation form
*/
protected def createFormStrongParameters: Seq[(String, ParamType)]
/**
* Executes resource creation.
*
* @param parameters permitted parameters
* @return generated resource id
*/
protected def doCreateAndReturnId(parameters: PermittedStrongParameters): Id = {
debugLoggingPermittedParameters(parameters)
model.createNewModel(parameters)
}
/**
* Creates new resource.
*
* POST /{resources}
*
* @param format format
* @return created response if success
*/
def createResource()(implicit format: Format = Format.HTML): Any = withFormat(format) {
debugLoggingParameters(createForm)
if (createForm.validate()) {
val id = if (!createFormStrongParameters.isEmpty) {
val parameters = createParams.permit(createFormStrongParameters: _*)
doCreateAndReturnId(parameters)
} else {
throw new StrongParametersException(
"'createFormStrongParameters' or 'createFormTypedStrongParameters' must be defined."
)
}
status = 201
val ext = if (format == Format.HTML) "" else "." + format.name
response(context).setHeader("Location", s"${contextPath}/${normalizedResourcesBasePath}/${model.idToRawValue(id)}${ext}")
} else {
status = 400
renderWithFormat(keyAndErrorMessages)
}
}
/**
* Input form for modification
*/
protected def updateForm: MapValidator
/**
* Params for modification.
*/
protected def updateParams: Params = Params(params(context))
/**
* Strong parameter definitions for modification form
*/
protected def updateFormStrongParameters: Seq[(String, ParamType)]
/**
* Executes modification for the specified resource.
*
* @param id id
* @param parameters permitted parameters
* @return count
*/
protected def doUpdate(id: Id, parameters: PermittedStrongParameters): Int = {
debugLoggingPermittedParameters(parameters, Some(id))
model.updateModelById(id, parameters)
}
/**
* Updates the specified single resource.
*
* PUT /{resources}/{id}
*
* @param id id
* @param format format
* @return result
*/
def updateResource(id: Id)(implicit format: Format = Format.HTML): Any = withFormat(format) {
debugLoggingParameters(updateForm, Some(id))
findResource(id).map { m =>
if (updateForm.validate()) {
if (!updateFormStrongParameters.isEmpty) {
val parameters = updateParams.permit(updateFormStrongParameters: _*)
doUpdate(id, parameters)
} else {
throw new StrongParametersException(
"'updateFormStrongParameters' or 'updateFormTypedStrongParameters' must be defined."
)
}
status = 200
} else {
status = 400
renderWithFormat(keyAndErrorMessages)
}
} getOrElse haltWithBody(404)(context)
}
/**
* Executes deletion of the specified single resource.
*
* @param id id
* @return count
*/
protected def doDestroy(id: Id): Int = {
model.deleteModelById(id)
}
/**
* Destroys the specified resource.
*
* DELETE /{resources}/{id}
*
* @param id id
* @param format format
* @return result
*/
def destroyResource(id: Id)(implicit format: Format = Format.HTML): Any = withFormat(format) {
findResource(id).map { m =>
doDestroy(id)
status = 200
} getOrElse haltWithBody(404)(context)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy