Dynamic variables and utility functions/macros for writing API functions.
Dynamic variables and utility functions/macros for writing API functions.
Delay that returns the User
(or nil) associated with the current API call.
ex. @*current-user*
Delay that returns the `User` (or nil) associated with the current API call. ex. `@*current-user*`
Int ID or nil
of user associated with current API call.
Int ID or `nil` of user associated with current API call.
Delay to the set of permissions granted to the current user.
Delay to the set of permissions granted to the current user.
Is the current user a superuser?
Is the current user a superuser?
(+check-superuser handler)
Wrap a Ring handler to make sure the current user is a superuser before handling any requests.
(api/+check-superuser routes)
Wrap a Ring handler to make sure the current user is a superuser before handling any requests. (api/+check-superuser routes)
(api-let [status-code message] [binding test] & body)
If TEST is true, bind it to BINDING and evaluate BODY.
(api-let [404 "Not found."] [user @current-user] (:id user))
If TEST is true, bind it to BINDING and evaluate BODY. (api-let [404 "Not found."] [user @*current-user*] (:id user))
(catch-and-raise raise body & more)
Catches exceptions thrown in body
and passes them along to the raise
function. Meant for writing async
endpoints.
You only need to raise
Exceptions that happen outside the initial thread of the API endpoint function; things like
normal permissions checks are usually done within the same thread that called the endpoint, meaning the middleware
that catches Exceptions will automatically handle them.
Catches exceptions thrown in `body` and passes them along to the `raise` function. Meant for writing async endpoints. You only need to `raise` Exceptions that happen outside the initial thread of the API endpoint function; things like normal permissions checks are usually done within the same thread that called the endpoint, meaning the middleware that catches Exceptions will automatically handle them.
(check tst code-or-code-message-pair & rest-args)
Assertion mechanism for use inside API functions.
Checks that TEST is true, or throws an ExceptionInfo
with STATUS-CODE and MESSAGE.
MESSAGE can be either a plain string error message, or a map including the key :message
and any additional
details, such as an :error_code
.
This exception is automatically caught in the body of defendpoint
functions, and the appropriate HTTP response is
generated.
check
can be called with the form
(check test code message)
or with the form
(check test [code message])
You can also include multiple tests in a single call:
(check test1 code1 message1 test2 code2 message2)
Assertion mechanism for use inside API functions. Checks that TEST is true, or throws an `ExceptionInfo` with STATUS-CODE and MESSAGE. MESSAGE can be either a plain string error message, or a map including the key `:message` and any additional details, such as an `:error_code`. This exception is automatically caught in the body of `defendpoint` functions, and the appropriate HTTP response is generated. `check` can be called with the form (check test code message) or with the form (check test [code message]) You can also include multiple tests in a single call: (check test1 code1 message1 test2 code2 message2)
(check-400 arg)
Throw a 400
if arg
is false
or nil
, otherwise return as-is.
Throw a `400` if `arg` is `false` or `nil`, otherwise return as-is.
(check-403 arg)
Throw a 403
(no permissions) if arg
is false
or nil
, otherwise return as-is.
Throw a `403` (no permissions) if `arg` is `false` or `nil`, otherwise return as-is.
(check-404 arg)
Throw a 404
if arg
is false
or nil
, otherwise return as-is.
Throw a `404` if `arg` is `false` or `nil`, otherwise return as-is.
(check-500 arg)
Throw a 500
if arg
is false
or nil
, otherwise return as-is.
Throw a `500` if `arg` is `false` or `nil`, otherwise return as-is.
(check-embedding-enabled)
Is embedding of Cards or Objects (secured access via /api/embed
endpoints with a signed JWT enabled?
Is embedding of Cards or Objects (secured access via `/api/embed` endpoints with a signed JWT enabled?
(check-exists? entity id)
(check-exists? entity k v & more)
Check that object with ID (or other key/values) exists in the DB, or throw a 404.
Check that object with ID (or other key/values) exists in the DB, or throw a 404.
(check-not-archived object)
Check that the OBJECT exists and is not :archived
, or throw a 404
. Returns OBJECT as-is if check passes.
Check that the OBJECT exists and is not `:archived`, or throw a `404`. Returns OBJECT as-is if check passes.
(check-public-sharing-enabled)
Check that the public-sharing-enabled
Setting is true
, or throw a 400
.
Check that the `public-sharing-enabled` Setting is `true`, or throw a `400`.
(check-superuser)
Check that *current-user*
is a superuser or throw a 403. This doesn't require a DB call.
Check that `*current-user*` is a superuser or throw a 403. This doesn't require a DB call.
(checkp tst field-name message)
Assertion mechanism for use inside API functions that validates individual input params.
Checks that TEST is true, or throws an ExceptionInfo
with FIELD-NAME and MESSAGE.
This exception is automatically caught in the body of defendpoint
functions, and the appropriate HTTP response is
generated.
checkp
can be called with the form
(checkp test field-name message)
Assertion mechanism for use inside API functions that validates individual input params. Checks that TEST is true, or throws an `ExceptionInfo` with FIELD-NAME and MESSAGE. This exception is automatically caught in the body of `defendpoint` functions, and the appropriate HTTP response is generated. `checkp` can be called with the form (checkp test field-name message)
(checkp-contains? valid-values-set symb value)
Check that the VALUE of parameter SYMB is in VALID-VALUES, or throw a 400. Returns VALUE upon success.
(checkp-contains? #{:fav :all :mine} 'f f) -> (check (contains? #{:fav :all :mine} f) [400 (str "Invalid value '" f "' for 'f': must be one of: #{:fav :all :mine}")])
Check that the VALUE of parameter SYMB is in VALID-VALUES, or throw a 400. Returns VALUE upon success. (checkp-contains? #{:fav :all :mine} 'f f) -> (check (contains? #{:fav :all :mine} f) [400 (str "Invalid value '" f "' for 'f': must be one of: #{:fav :all :mine}")])
(checkp-with f symb value)
(checkp-with f symb value message)
Check (F VALUE), or throw an exception with STATUS-CODE (default is 400). SYMB is passed in order to give the user a relevant error message about which parameter was bad.
Returns VALUE upon success.
(checkp-with (partial? contains? {:all :mine}) f :all) -> :all (checkp-with (partial? contains {:all :mine}) f :bad) -> ExceptionInfo: Invalid value ':bad' for 'f': test failed: (partial? contains?) {:all :mine}
You may optionally pass a MESSAGE to append to the exception upon failure; this will be used in place of the "test failed: ..." message.
MESSAGE may be either a string or a pair like [status-code message]
.
Check (F VALUE), or throw an exception with STATUS-CODE (default is 400). SYMB is passed in order to give the user a relevant error message about which parameter was bad. Returns VALUE upon success. (checkp-with (partial? contains? {:all :mine}) f :all) -> :all (checkp-with (partial? contains {:all :mine}) f :bad) -> ExceptionInfo: Invalid value ':bad' for 'f': test failed: (partial? contains?) {:all :mine} You may optionally pass a MESSAGE to append to the exception upon failure; this will be used in place of the "test failed: ..." message. MESSAGE may be either a string or a pair like `[status-code message]`.
(column-will-change? k object-before-updates object-updates)
Inputs: [k :- s/Keyword object-before-updates :- su/Map object-updates :- su/Map] Returns: s/Bool
Helper for PATCH-style operations to see if a column is set to change when object-updates
(i.e., the input to the
endpoint) is applied.
;; assuming we have a Collection 10, that is not currently archived... (api/column-will-change? :archived (Collection 10) {:archived true}) ; -> true, because value will change
(api/column-will-change? :archived (Collection 10) {:archived false}) ; -> false, because value did not change
(api/column-will-change? :archived (Collection 10) {}) ; -> false; value not specified in updates (request body)
Inputs: [k :- s/Keyword object-before-updates :- su/Map object-updates :- su/Map] Returns: s/Bool Helper for PATCH-style operations to see if a column is set to change when `object-updates` (i.e., the input to the endpoint) is applied. ;; assuming we have a Collection 10, that is not currently archived... (api/column-will-change? :archived (Collection 10) {:archived true}) ; -> true, because value will change (api/column-will-change? :archived (Collection 10) {:archived false}) ; -> false, because value did not change (api/column-will-change? :archived (Collection 10) {}) ; -> false; value not specified in updates (request body)
(create-check entity m)
NEW! Check whether the current user has permissions to CREATE a new instance of an object with properties in map m
.
This function was added years after read-check
and write-check
, and at the time of this writing most models do
not implement this method. Most POST
API endpoints instead have the can-create?
logic for a given model
hardcoded into this -- this should be considered an antipattern and be refactored out going forward.
NEW! Check whether the current user has permissions to CREATE a new instance of an object with properties in map `m`. This function was added *years* after `read-check` and `write-check`, and at the time of this writing most models do not implement this method. Most `POST` API endpoints instead have the `can-create?` logic for a given model hardcoded into this -- this should be considered an antipattern and be refactored out going forward.
(defendpoint method route docstr? args schemas-map? & body)
Define an API function. This automatically does several things:
calls auto-parse
to automatically parse certain args. e.g. id
is converted from String
to Integer
via
Integer/parseInt
converts ROUTE from a simple form like "/:id"
to a typed one like ["/:id" :id #"[0-9]+"]
sequentially applies specified annotation functions on args to validate them.
automatically calls wrap-response-if-needed
on the result of BODY
tags function's metadata in a way that subsequent calls to define-routes
(see below) will automatically include
the function in the generated defroutes
form.
Generates a super-sophisticated Markdown-formatted docstring
Define an API function. This automatically does several things: - calls `auto-parse` to automatically parse certain args. e.g. `id` is converted from `String` to `Integer` via `Integer/parseInt` - converts ROUTE from a simple form like `"/:id"` to a typed one like `["/:id" :id #"[0-9]+"]` - sequentially applies specified annotation functions on args to validate them. - automatically calls `wrap-response-if-needed` on the result of BODY - tags function's metadata in a way that subsequent calls to `define-routes` (see below) will automatically include the function in the generated `defroutes` form. - Generates a super-sophisticated Markdown-formatted docstring
(defendpoint-async method route docstr? args schemas-map? & body)
Like defendpoint
, but generates an endpoint that accepts the usual [request respond raise]
params.
Like `defendpoint`, but generates an endpoint that accepts the usual `[request respond raise]` params.
(define-routes & middleware)
Create a (defroutes routes ...)
form that automatically includes all functions created with defendpoint
in the
current namespace. Optionally specify middleware that will apply to all of the endpoints in the current namespace.
(api/define-routes api/+check-superuser) ; all API endpoints in this namespace will require superuser access
Create a `(defroutes routes ...)` form that automatically includes all functions created with `defendpoint` in the current namespace. Optionally specify middleware that will apply to all of the endpoints in the current namespace. (api/define-routes api/+check-superuser) ; all API endpoints in this namespace will require superuser access
A 'No Content' response for DELETE
endpoints to return.
A 'No Content' response for `DELETE` endpoints to return.
(let-400 & body)
Bind a form as with let
; throw a 400 if it is nil
or false
.
Bind a form as with `let`; throw a 400 if it is `nil` or `false`.
(let-403 & body)
Bind a form as with let
; throw a 403 if it is nil
or false
.
Bind a form as with `let`; throw a 403 if it is `nil` or `false`.
(let-404 & body)
Bind a form as with let
; throw a 404 if it is nil
or false
.
Bind a form as with `let`; throw a 404 if it is `nil` or `false`.
(let-500 & body)
Bind a form as with let
; throw a 500 if it is nil
or false
.
Bind a form as with `let`; throw a 500 if it is `nil` or `false`.
(maybe-reconcile-collection-position! new-model-data)
(maybe-reconcile-collection-position! {old-collection-id :collection_id
old-position :collection_position
:as before-update}
{new-collection-id :collection_id
new-position :collection_position
:as model-updates})
Inputs: ([new-model-data :- ModelWithPosition] [{old-collection-id :collection_id, old-position :collection_position, :as before-update} :- (s/maybe ModelWithPosition) {new-collection-id :collection_id, new-position :collection_position, :as model-updates} :- ModelWithOptionalPosition])
Generic function for working on cards/dashboards/pulses. Checks the before and after changes to see if there is any impact to the collection position of that model instance. If so, executes updates to fix the collection position that goes with the change. The 2-arg version of this function is used for a new card/dashboard/pulse (i.e. not updating an existing instance, but creating a new one).
Inputs: ([new-model-data :- ModelWithPosition] [{old-collection-id :collection_id, old-position :collection_position, :as before-update} :- (s/maybe ModelWithPosition) {new-collection-id :collection_id, new-position :collection_position, :as model-updates} :- ModelWithOptionalPosition]) Generic function for working on cards/dashboards/pulses. Checks the before and after changes to see if there is any impact to the collection position of that model instance. If so, executes updates to fix the collection position that goes with the change. The 2-arg version of this function is used for a new card/dashboard/pulse (i.e. not updating an existing instance, but creating a new one).
(read-check obj)
(read-check entity id)
(read-check entity id & other-conditions)
Check whether we can read an existing OBJ, or ENTITY with ID. If the object doesn't exist, throw a 404; if we don't have proper permissions, throw a 403. This will fetch the object if it was not already fetched, and returns OBJ if the check is successful.
Check whether we can read an existing OBJ, or ENTITY with ID. If the object doesn't exist, throw a 404; if we don't have proper permissions, throw a 403. This will fetch the object if it was not already fetched, and returns OBJ if the check is successful.
(reconcile-position-for-collection! collection-id old-position new-position)
Inputs: [collection-id :- (s/maybe su/IntGreaterThanZero) old-position :- (s/maybe su/IntGreaterThanZero) new-position :- (s/maybe su/IntGreaterThanZero)]
Compare old-position
and new-position
to determine what needs to be updated based on the position change. Used
for fixing card/dashboard/pulse changes that impact other instances in the collection
Inputs: [collection-id :- (s/maybe su/IntGreaterThanZero) old-position :- (s/maybe su/IntGreaterThanZero) new-position :- (s/maybe su/IntGreaterThanZero)] Compare `old-position` and `new-position` to determine what needs to be updated based on the position change. Used for fixing card/dashboard/pulse changes that impact other instances in the collection
(throw-403)
Throw a generic 403 (no permissions) error response.
Throw a generic 403 (no permissions) error response.
(throw-invalid-param-exception field-name message)
Throw an ExceptionInfo
that contains information about an invalid API params in the expected format.
Throw an `ExceptionInfo` that contains information about an invalid API params in the expected format.
(write-check obj)
(write-check entity id)
(write-check entity id & other-conditions)
Check whether we can write an existing OBJ, or ENTITY with ID. If the object doesn't exist, throw a 404; if we don't have proper permissions, throw a 403. This will fetch the object if it was not already fetched, and returns OBJ if the check is successful.
Check whether we can write an existing OBJ, or ENTITY with ID. If the object doesn't exist, throw a 404; if we don't have proper permissions, throw a 403. This will fetch the object if it was not already fetched, and returns OBJ if the check is successful.
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close