Skip to content

Commit 54cdd49

Browse files
authored
Merge pull request #179 from noir-cr/dev
Release 0.11.0
2 parents 3c13254 + 93b530c commit 54cdd49

File tree

82 files changed

+1306
-129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1306
-129
lines changed

.ameba.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ ignored_paths:
33

44
Metrics/CyclomaticComplexity:
55
Description: Disallows methods with a cyclomatic complexity higher than `MaxComplexity`
6-
MaxComplexity: 35
6+
MaxComplexity: 40
77
Enabled: true
88
Severity: Warning

.github/workflows/homebrew_publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
- name: Release Noir to Homebrew tap
1313
uses: Justintime50/homebrew-releaser@v1
1414
with:
15-
homebrew_owner: hahwul
15+
homebrew_owner: noir-cr
1616
homebrew_tap: homebrew-noir
1717
formula_folder: Formula
1818

README.md

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,33 @@
1515
- Method
1616
- Param
1717
- Header
18+
- Cookie
1819
- Protocol (e.g ws)
1920

2021
### Languages and Frameworks
2122

22-
| Language | Framework | URL | Method | Param | Header | WS |
23-
|----------|-----------------|-----|--------|-------|--------|----|
24-
| Crystal | Kemal ||||||
25-
| Go | Echo ||||| X |
26-
| Go | Gin ||||| X |
27-
| Python | Django ||||| X |
28-
| Python | Flask ||||| X |
29-
| Python | FastAPI ||||||
30-
| Ruby | Rails ||||| X |
31-
| Ruby | Sinatra ||||| X |
32-
| Php | ||||| X |
33-
| Java | Jsp |||| X | X |
34-
| Java | Armeria ||| X | X | X |
35-
| Java | Spring ||| X | X | X |
36-
| Kotlin | Spring ||| X | X | X |
37-
| JS | Express ||| X | X | X |
38-
| Rust | Axum ||| X | X | X |
39-
| C# | ASP.NET MVC || X | X | X | X |
40-
| JS | Next | X | X | X | X | X |
23+
| Language | Framework | URL | Method | Param | Header | Cookie | WS |
24+
|----------|-------------|-----|--------|-------|--------|--------|----|
25+
| Crystal | Kemal |||||||
26+
| Crystal | Lucky |||||| X |
27+
| Go | Echo |||||| X |
28+
| Go | Gin |||||| X |
29+
| Python | Django |||||| X |
30+
| Python | Flask |||||| X |
31+
| Python | FastAPI |||||||
32+
| Ruby | Rails |||||| X |
33+
| Ruby | Sinatra |||||| X |
34+
| Ruby | Hanami ||| X | X | X | X |
35+
| Php | ||||| X | X |
36+
| Java | Jsp |||| X | X | X |
37+
| Java | Armeria ||| X | X | X | X |
38+
| Java | Spring ||| X | X | X | X |
39+
| Kotlin | Spring ||| X | X | X | X |
40+
| JS | Express ||| X | X | X | X |
41+
| Rust | Axum ||| X | X | X | X |
42+
| Elixir | Phoenix ||| X | X | X ||
43+
| C# | ASP.NET MVC || X | X | X | X | X |
44+
| JS | Next | X | X | X | X | X | X |
4145

4246

4347
### Specification

shard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: noir
2-
version: 0.10.0
2+
version: 0.11.0
33

44
authors:
55
- hahwul <hahwul@gmail.com>

spec/functional_test/fixtures/kemal/src/testapp.cr renamed to spec/functional_test/fixtures/crystal_kemal/src/testapp.cr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
require "kemal"
22

33
get "/" do
4+
env.request.headers["x-api-key"].as(String)
45
"Hello World!"
56
end
67

78
post "/query" do
9+
env.request.cookies["my_auth"].as(String)
810
env.params.body["query"].as(String)
911
end
1012

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require "./server"
2+
3+
Authentic.configure do |settings|
4+
settings.secret_key = Lucky::Server.settings.secret_key_base
5+
6+
unless LuckyEnv.production?
7+
# This value can be between 4 and 31
8+
fastest_encryption_possible = 4
9+
settings.encryption_cost = fastest_encryption_possible
10+
end
11+
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require "./server"
2+
3+
Lucky::Session.configure do |settings|
4+
settings.key = "_crystal_lucky_session"
5+
end
6+
7+
Lucky::CookieJar.configure do |settings|
8+
settings.on_set = ->(cookie : HTTP::Cookie) {
9+
# If ForceSSLHandler is enabled, only send cookies over HTTPS
10+
cookie.secure(Lucky::ForceSSLHandler.settings.enabled)
11+
12+
# By default, don't allow reading cookies with JavaScript
13+
cookie.http_only(true)
14+
15+
# Restrict cookies to a first-party or same-site context
16+
cookie.samesite(:lax)
17+
18+
# Set all cookies to the root path by default
19+
cookie.path("/")
20+
21+
# You can set other defaults for cookies here. For example:
22+
#
23+
# cookie.expires(1.year.from_now).domain("mydomain.com")
24+
}
25+
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
name: crystal_lucky
3+
version: 0.1.0
4+
targets:
5+
crystal_lucky:
6+
main: src/crystal_lucky.cr
7+
crystal: '>= 1.10.1'
8+
dependencies:
9+
lucky:
10+
github: luckyframework/lucky
11+
version: ~> 1.1.0
12+
avram:
13+
github: luckyframework/avram
14+
version: ~> 1.1.0
15+
carbon:
16+
github: luckyframework/carbon
17+
version: ~> 0.4.0
18+
carbon_sendgrid_adapter:
19+
github: luckyframework/carbon_sendgrid_adapter
20+
version: ~> 0.4.0
21+
lucky_env:
22+
github: luckyframework/lucky_env
23+
version: ~> 0.2.0
24+
lucky_task:
25+
github: luckyframework/lucky_task
26+
version: ~> 0.3.0
27+
authentic:
28+
github: luckyframework/authentic
29+
version: '>= 1.0.0, < 2.0.0'
30+
jwt:
31+
github: crystal-community/jwt
32+
version: ~> 1.6.0
33+
development_dependencies: {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Api::Me::Show < ApiAction
2+
get "/api/me" do
3+
remote_ip = request.headers["X-Forwarded-For"]
4+
_ = remote_ip
5+
params.from_query["q"] # => "Lucky"
6+
params.get("query")
7+
params.get(:filter)
8+
json UserSerializer.new(current_user)
9+
end
10+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Api::SignIns::Create < ApiAction
2+
include Api::Auth::SkipRequireAuthToken
3+
4+
post "/api/sign_ins" do
5+
SignInUser.run(params) do |operation, user|
6+
params.from_json["users"]
7+
if user
8+
json({token: UserToken.generate(user)})
9+
else
10+
raise Avram::InvalidOperationError.new(operation)
11+
end
12+
end
13+
end
14+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Api::SignUps::Create < ApiAction
2+
include Api::Auth::SkipRequireAuthToken
3+
4+
post "/api/sign_ups" do
5+
user = SignUpUser.create!(params)
6+
cookies.get("name1")
7+
cookies["name2"]
8+
cookies.get_raw("name3")
9+
10+
json({token: UserToken.generate(user)})
11+
end
12+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Include modules and add methods that are for all API requests
2+
abstract class ApiAction < Lucky::Action
3+
# APIs typically do not need to send cookie/session data.
4+
# Remove this line if you want to send cookies in the response header.
5+
disable_cookies
6+
accepted_formats [:json]
7+
8+
include Api::Auth::Helpers
9+
10+
# By default all actions require sign in.
11+
# Add 'include Api::Auth::SkipRequireAuthToken' to your actions to allow all requests.
12+
include Api::Auth::RequireAuthToken
13+
14+
# By default all actions are required to use underscores to separate words.
15+
# Add 'include Lucky::SkipRouteStyleCheck' to your actions if you wish to ignore this check for specific routes.
16+
include Lucky::EnforceUnderscoredRoute
17+
end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# This class handles error responses and reporting.
2+
#
3+
# https://luckyframework.org/guides/http-and-routing/error-handling
4+
class Errors::Show < Lucky::ErrorAction
5+
DEFAULT_MESSAGE = "Something went wrong."
6+
default_format :json
7+
dont_report [Lucky::RouteNotFoundError, Avram::RecordNotFoundError]
8+
9+
def render(error : Lucky::RouteNotFoundError | Avram::RecordNotFoundError)
10+
error_json "Not found", status: 404
11+
end
12+
13+
# When an InvalidOperationError is raised, show a helpful error with the
14+
# param that is invalid, and what was wrong with it.
15+
def render(error : Avram::InvalidOperationError)
16+
error_json \
17+
message: error.renderable_message,
18+
details: error.renderable_details,
19+
param: error.invalid_attribute_name,
20+
status: 400
21+
end
22+
23+
# Always keep this below other 'render' methods or it may override your
24+
# custom 'render' methods.
25+
def render(error : Lucky::RenderableError)
26+
error_json error.renderable_message, status: error.renderable_status
27+
end
28+
29+
# If none of the 'render' methods return a response for the raised Exception,
30+
# Lucky will use this method.
31+
def default_render(error : Exception) : Lucky::Response
32+
error_json DEFAULT_MESSAGE, status: 500
33+
end
34+
35+
private def error_json(message : String, status : Int, details = nil, param = nil)
36+
json ErrorSerializer.new(message: message, details: details, param: param), status: status
37+
end
38+
39+
private def report(error : Exception) : Nil
40+
# Send to Rollbar, send an email, etc.
41+
end
42+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Home::Index < ApiAction
2+
include Api::Auth::SkipRequireAuthToken
3+
4+
get "/" do
5+
json({hello: "Hello World from Home::Index"})
6+
end
7+
end

spec/functional_test/fixtures/crystal_lucky/src/actions/mixins/.keep

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Api::Auth::Helpers
2+
# The 'memoize' macro makes sure only one query is issued to find the user
3+
memoize def current_user? : User?
4+
auth_token.try do |value|
5+
user_from_auth_token(value)
6+
end
7+
end
8+
9+
private def auth_token : String?
10+
bearer_token || token_param
11+
end
12+
13+
private def bearer_token : String?
14+
context.request.headers["Authorization"]?
15+
.try(&.gsub("Bearer", ""))
16+
.try(&.strip)
17+
end
18+
19+
private def token_param : String?
20+
params.get?(:auth_token)
21+
end
22+
23+
private def user_from_auth_token(token : String) : User?
24+
UserToken.decode_user_id(token).try do |user_id|
25+
UserQuery.new.id(user_id).first?
26+
end
27+
end
28+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module Api::Auth::RequireAuthToken
2+
macro included
3+
before require_auth_token
4+
end
5+
6+
private def require_auth_token
7+
if current_user?
8+
continue
9+
else
10+
json auth_error_json, 401
11+
end
12+
end
13+
14+
private def auth_error_json
15+
ErrorSerializer.new(
16+
message: "Not authenticated.",
17+
details: auth_error_details
18+
)
19+
end
20+
21+
private def auth_error_details : String
22+
if auth_token
23+
"The provided authentication token was incorrect."
24+
else
25+
"An authentication token is required. Please include a token in an 'auth_token' param or 'Authorization' header."
26+
end
27+
end
28+
29+
# Tells the compiler that the current_user is not nil since we have checked
30+
# that the user is signed in
31+
private def current_user : User
32+
current_user?.as(User)
33+
end
34+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module Api::Auth::SkipRequireAuthToken
2+
macro included
3+
skip require_auth_token
4+
end
5+
6+
# Since sign in is not required, current_user might be nil
7+
def current_user : User?
8+
current_user?
9+
end
10+
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# This file loads your app and all your tasks when running 'lucky'
2+
#
3+
# Run 'lucky --help' to see all available tasks.
4+
#
5+
# Learn to create your own tasks:
6+
# https://luckyframework.org/guides/command-line-tasks/custom-tasks
7+
8+
# See `LuckyEnv#task?`
9+
ENV["LUCKY_TASK"] = "true"
10+
11+
# Load Lucky and the app (actions, models, etc.)
12+
require "./src/app"
13+
require "lucky_task"
14+
15+
# You can add your own tasks here in the ./tasks folder
16+
require "./tasks/**"
17+
18+
# Load migrations
19+
require "./db/migrations/**"
20+
21+
# Load Lucky tasks (dev, routes, etc.)
22+
require "lucky/tasks/**"
23+
require "avram/lucky/tasks"
24+
25+
LuckyTask::Runner.run

0 commit comments

Comments
 (0)