summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorOmar Roth <omarroth@protonmail.com>2019-05-30 18:31:22 -0500
committerOmar Roth <omarroth@protonmail.com>2019-05-30 19:00:38 -0500
commitda48bbf31237b354a8dd94bd7550f4548437cb1c (patch)
tree7041f85eb1ec32ad31520db59e76de4f86c74747 /src
parentac957db6d1f54f730f2ef08bc915fa52265e463e (diff)
downloadinvidious-da48bbf31237b354a8dd94bd7550f4548437cb1c.tar.gz
invidious-da48bbf31237b354a8dd94bd7550f4548437cb1c.tar.bz2
invidious-da48bbf31237b354a8dd94bd7550f4548437cb1c.zip
Add support for partial POST to '/api/v1/auth/preferences'
Diffstat (limited to 'src')
-rw-r--r--src/invidious.cr4
-rw-r--r--src/invidious/helpers/macros.cr54
-rw-r--r--src/invidious/helpers/patch_mapping.cr166
3 files changed, 195 insertions, 29 deletions
diff --git a/src/invidious.cr b/src/invidious.cr
index 01b1063a..7ff3fb3d 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -1880,7 +1880,7 @@ post "/data_control" do |env|
end
if body["preferences"]?
- user.preferences = Preferences.from_json(body["preferences"].to_json)
+ user.preferences = Preferences.from_json(body["preferences"].to_json, user.preferences)
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", user.preferences.to_json, user.email)
end
when "import_youtube"
@@ -4468,7 +4468,7 @@ post "/api/v1/auth/preferences" do |env|
user = env.get("user").as(User)
begin
- preferences = Preferences.from_json(env.request.body || "{}")
+ preferences = Preferences.from_json(env.request.body || "{}", user.preferences)
rescue
preferences = user.preferences
end
diff --git a/src/invidious/helpers/macros.cr b/src/invidious/helpers/macros.cr
index fe1fc94e..ddfb9f8e 100644
--- a/src/invidious/helpers/macros.cr
+++ b/src/invidious/helpers/macros.cr
@@ -1,49 +1,49 @@
macro db_mapping(mapping)
- def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
- end
+ def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
+ end
- def to_a
- return [ {{*mapping.keys.map { |id| "@#{id}".id }}} ]
- end
+ def to_a
+ return [ {{*mapping.keys.map { |id| "@#{id}".id }}} ]
+ end
- def self.to_type_tuple
- return { {{*mapping.keys.map { |id| "#{id}" }}} }
- end
+ def self.to_type_tuple
+ return { {{*mapping.keys.map { |id| "#{id}" }}} }
+ end
- DB.mapping( {{mapping}} )
+ DB.mapping( {{mapping}} )
end
macro json_mapping(mapping)
- def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
- end
+ def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
+ end
- def to_a
- return [ {{*mapping.keys.map { |id| "@#{id}".id }}} ]
- end
+ def to_a
+ return [ {{*mapping.keys.map { |id| "@#{id}".id }}} ]
+ end
- JSON.mapping( {{mapping}} )
- YAML.mapping( {{mapping}} )
+ patched_json_mapping( {{mapping}} )
+ YAML.mapping( {{mapping}} )
end
macro yaml_mapping(mapping)
- def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
- end
+ def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
+ end
- def to_a
- return [ {{*mapping.keys.map { |id| "@#{id}".id }}} ]
- end
+ def to_a
+ return [ {{*mapping.keys.map { |id| "@#{id}".id }}} ]
+ end
- def to_tuple
- return { {{*mapping.keys.map { |id| "@#{id}".id }}} }
- end
+ def to_tuple
+ return { {{*mapping.keys.map { |id| "@#{id}".id }}} }
+ end
- YAML.mapping({{mapping}})
+ YAML.mapping({{mapping}})
end
macro templated(filename, template = "template")
- render "src/invidious/views/#{{{filename}}}.ecr", "src/invidious/views/#{{{template}}}.ecr"
+ render "src/invidious/views/#{{{filename}}}.ecr", "src/invidious/views/#{{{template}}}.ecr"
end
macro rendered(filename)
- render "src/invidious/views/#{{{filename}}}.ecr"
+ render "src/invidious/views/#{{{filename}}}.ecr"
end
diff --git a/src/invidious/helpers/patch_mapping.cr b/src/invidious/helpers/patch_mapping.cr
new file mode 100644
index 00000000..8360caa6
--- /dev/null
+++ b/src/invidious/helpers/patch_mapping.cr
@@ -0,0 +1,166 @@
+# Overloads https://github.com/crystal-lang/crystal/blob/0.28.0/src/json/from_json.cr#L24
+def Object.from_json(string_or_io, default) : self
+ parser = JSON::PullParser.new(string_or_io)
+ new parser, default
+end
+
+# Adds configurable 'default' to
+macro patched_json_mapping(_properties_, strict = false)
+ {% for key, value in _properties_ %}
+ {% _properties_[key] = {type: value} unless value.is_a?(HashLiteral) || value.is_a?(NamedTupleLiteral) %}
+ {% end %}
+
+ {% for key, value in _properties_ %}
+ {% _properties_[key][:key_id] = key.id.gsub(/\?$/, "") %}
+ {% end %}
+
+ {% for key, value in _properties_ %}
+ @{{value[:key_id]}} : {{value[:type]}}{{ (value[:nilable] ? "?" : "").id }}
+
+ {% if value[:setter] == nil ? true : value[:setter] %}
+ def {{value[:key_id]}}=(_{{value[:key_id]}} : {{value[:type]}}{{ (value[:nilable] ? "?" : "").id }})
+ @{{value[:key_id]}} = _{{value[:key_id]}}
+ end
+ {% end %}
+
+ {% if value[:getter] == nil ? true : value[:getter] %}
+ def {{key.id}} : {{value[:type]}}{{ (value[:nilable] ? "?" : "").id }}
+ @{{value[:key_id]}}
+ end
+ {% end %}
+
+ {% if value[:presence] %}
+ @{{value[:key_id]}}_present : Bool = false
+
+ def {{value[:key_id]}}_present?
+ @{{value[:key_id]}}_present
+ end
+ {% end %}
+ {% end %}
+
+ def initialize(%pull : ::JSON::PullParser, default = nil)
+ {% for key, value in _properties_ %}
+ %var{key.id} = nil
+ %found{key.id} = false
+ {% end %}
+
+ %location = %pull.location
+ begin
+ %pull.read_begin_object
+ rescue exc : ::JSON::ParseException
+ raise ::JSON::MappingError.new(exc.message, self.class.to_s, nil, *%location, exc)
+ end
+ while %pull.kind != :end_object
+ %key_location = %pull.location
+ key = %pull.read_object_key
+ case key
+ {% for key, value in _properties_ %}
+ when {{value[:key] || value[:key_id].stringify}}
+ %found{key.id} = true
+ begin
+ %var{key.id} =
+ {% if value[:nilable] || value[:default] != nil %} %pull.read_null_or { {% end %}
+
+ {% if value[:root] %}
+ %pull.on_key!({{value[:root]}}) do
+ {% end %}
+
+ {% if value[:converter] %}
+ {{value[:converter]}}.from_json(%pull)
+ {% elsif value[:type].is_a?(Path) || value[:type].is_a?(Generic) %}
+ {{value[:type]}}.new(%pull)
+ {% else %}
+ ::Union({{value[:type]}}).new(%pull)
+ {% end %}
+
+ {% if value[:root] %}
+ end
+ {% end %}
+
+ {% if value[:nilable] || value[:default] != nil %} } {% end %}
+ rescue exc : ::JSON::ParseException
+ raise ::JSON::MappingError.new(exc.message, self.class.to_s, {{value[:key] || value[:key_id].stringify}}, *%key_location, exc)
+ end
+ {% end %}
+ else
+ {% if strict %}
+ raise ::JSON::MappingError.new("Unknown JSON attribute: #{key}", self.class.to_s, nil, *%key_location, nil)
+ {% else %}
+ %pull.skip
+ {% end %}
+ end
+ end
+ %pull.read_next
+
+ {% for key, value in _properties_ %}
+ {% unless value[:nilable] || value[:default] != nil %}
+ if %var{key.id}.nil? && !%found{key.id} && !::Union({{value[:type]}}).nilable?
+ raise ::JSON::MappingError.new("Missing JSON attribute: {{(value[:key] || value[:key_id]).id}}", self.class.to_s, nil, *%location, nil)
+ end
+ {% end %}
+
+ {% if value[:nilable] %}
+ {% if value[:default] != nil %}
+ @{{value[:key_id]}} = %found{key.id} ? %var{key.id} : (default.responds_to?(:{{value[:key_id]}}) ? default.{{value[:key_id]}} : {{value[:default]}})
+ {% else %}
+ @{{value[:key_id]}} = %var{key.id}
+ {% end %}
+ {% elsif value[:default] != nil %}
+ @{{value[:key_id]}} = %var{key.id}.nil? ? (default.responds_to?(:{{value[:key_id]}}) ? default.{{value[:key_id]}} : {{value[:default]}}) : %var{key.id}
+ {% else %}
+ @{{value[:key_id]}} = (%var{key.id}).as({{value[:type]}})
+ {% end %}
+
+ {% if value[:presence] %}
+ @{{value[:key_id]}}_present = %found{key.id}
+ {% end %}
+ {% end %}
+ end
+
+ def to_json(json : ::JSON::Builder)
+ json.object do
+ {% for key, value in _properties_ %}
+ _{{value[:key_id]}} = @{{value[:key_id]}}
+
+ {% unless value[:emit_null] %}
+ unless _{{value[:key_id]}}.nil?
+ {% end %}
+
+ json.field({{value[:key] || value[:key_id].stringify}}) do
+ {% if value[:root] %}
+ {% if value[:emit_null] %}
+ if _{{value[:key_id]}}.nil?
+ nil.to_json(json)
+ else
+ {% end %}
+
+ json.object do
+ json.field({{value[:root]}}) do
+ {% end %}
+
+ {% if value[:converter] %}
+ if _{{value[:key_id]}}
+ {{ value[:converter] }}.to_json(_{{value[:key_id]}}, json)
+ else
+ nil.to_json(json)
+ end
+ {% else %}
+ _{{value[:key_id]}}.to_json(json)
+ {% end %}
+
+ {% if value[:root] %}
+ {% if value[:emit_null] %}
+ end
+ {% end %}
+ end
+ end
+ {% end %}
+ end
+
+ {% unless value[:emit_null] %}
+ end
+ {% end %}
+ {% end %}
+ end
+ end
+end