From 5b2878604d6b9e55b32412453e6b8b6626cff027 Mon Sep 17 00:00:00 2001 From: Emma Lejeck Date: Thu, 24 Oct 2024 00:52:38 -0700 Subject: [PATCH] Switch from graphql-client to http.rb for Anilist import (Fixes SERVER-FHD) --- app/models/list_import/anilist.rb | 107 +++++++++++------------- app/models/list_import/anilist/row.rb | 47 +++++------ lib/anilist_api_wrapper.rb | 22 ----- spec/models/list_import/anilist_spec.rb | 10 +-- 4 files changed, 75 insertions(+), 111 deletions(-) delete mode 100644 lib/anilist_api_wrapper.rb diff --git a/app/models/list_import/anilist.rb b/app/models/list_import/anilist.rb index 971e3b15a1..5b60fc9b62 100644 --- a/app/models/list_import/anilist.rb +++ b/app/models/list_import/anilist.rb @@ -2,61 +2,8 @@ class ListImport class Anilist < ListImport - # accepts a username as input - validates :input_text, length: { - minimum: 3, - maximum: 20 - }, presence: true - # does not accept file uploads - validates :input_file_data, absence: true - validate :ensure_user_exists, on: :create - - def ensure_user_exists - return false if input_text.blank? - return true if user_exists? - - errors.add(:input_text, "AniList user not found - #{input_text}") - end - - def count - @count ||= anime_list.count + manga_list.count - end - - def each - %w[anime manga].each do |type| - send(:"#{type}_list").each do |media| - row = Row.new(media, type) - - yield row.media, row.data - end - end - end - - private - - def anime_list - media_lists.data.anime.lists.map(&:entries).flatten - end - - def manga_list - media_lists.data.manga.lists.map(&:entries).flatten - end - - def user_exists? - @user_exists ||= media_lists&.errors&.detect { |error| error.last.include?('404') }.blank? - end - - def media_lists - @media_lists ||= AnilistApiWrapper::Client.query( - self.class.media_list_query, - variables: { - user_name: input_text - } - ) - end - - def self.media_list_query - @media_list_query ||= AnilistApiWrapper::Client.parse <<-GRAPHQL + ANILIST_API = 'https://graphql.anilist.co' + MEDIA_LIST_QUERY = <<-GRAPHQL query($user_name: String) { anime: MediaListCollection(userName: $user_name, type: ANIME) { lists { @@ -129,7 +76,55 @@ def self.media_list_query } } } - GRAPHQL + GRAPHQL + + # accepts a username as input + validates :input_text, length: { + minimum: 3, + maximum: 20 + }, presence: true + # does not accept file uploads + validates :input_file_data, absence: true + validate :ensure_user_exists, on: :create + + def ensure_user_exists + return false if input_text.blank? + return true if user_exists? + + errors.add(:input_text, "AniList user not found - #{input_text}") + end + + def count + @count ||= list('anime').count + list('manga').count + end + + def each + %w[anime manga].each do |type| + list(type).each do |media| + row = Row.new(media, type) + + yield row.media, row.data + end + end + end + + private + + def list(type) + media_lists.dig('data', type, 'lists').flat_map { |list| list['entries'] } + end + + def user_exists? + @user_exists ||= media_lists['errors']&.detect { |error| error.last.include?('404') }.blank? + end + + def media_lists + @media_lists ||= Oj.load(HTTP.post(ANILIST_API, json: { + query: MEDIA_LIST_QUERY, + variables: { + user_name: input_text + } + }).body) end rescue StandardError => e diff --git a/app/models/list_import/anilist/row.rb b/app/models/list_import/anilist/row.rb index 00bdf60d30..90830a994c 100644 --- a/app/models/list_import/anilist/row.rb +++ b/app/models/list_import/anilist/row.rb @@ -25,7 +25,7 @@ def media end def data - fields.map { |field| [field, send(field)] }.to_h.compact + fields.index_with { |field| send(field) }.compact end private @@ -42,23 +42,23 @@ def fields # For mapping guess def media_info { - title: title, + title:, subtype: type, - episode_count: media_data.episodes, - chapter_count: media_data.chapters + episode_count: media_data['episodes'], + chapter_count: media_data['chapters'] }.compact end # 100-point scale to 20-point scale (raw) # rating -> score def rating - return nil if node.score.zero? + return nil if node['score'].zero? - [(node.score.to_f / 5).ceil, 2].max + [(node['score'].to_f / 5).ceil, 2].max end def status - case node.status.downcase + case node['status'].downcase when 'completed' then :completed when 'current' then :current when 'planning' then :planned @@ -69,41 +69,38 @@ def status # reconsume_count -> repeat def reconsume_count - node.repeat + node['repeat'] end def progress - node.progress + node['progress'] end def notes - node.notes + node['notes'] end # 2020-05-19 -> yyyy-mm-dd def started_at - return if node.started_at.to_h.compact.blank? - # will properly convert to include 0 before days/months - formatted_date(node.started_at) + formatted_date(node['startedAt']) end # finished_at -> completed_at def finished_at - return if node.completed_at.to_h.compact.blank? - # will properly convert to include 0 before days/months - formatted_date(node.completed_at) + formatted_date(node['completedAt']) end def formatted_date(date_node) - date_node.to_h.symbolize_keys.values_at(:year, :month, :day).join('-').to_date.to_s + return if date_node.compact.blank? + date_node.values_at('year', 'month', 'day').join('-').to_date.to_s end def volumes_owned return unless type == 'manga' - node.progress_volumes.presence || 0 + node['progressVolumes'].presence || 0 end def anilist_key @@ -115,22 +112,22 @@ def mal_key end def media_data - node.media + node['media'] end def mal_id - media_data.id_mal + media_data['idMal'] end def titles - media_data.title + media_data['title'] end def title - titles.romaji.presence || - titles.english.presence || - titles.native.presence || - titles.user_preferred + titles['romaji'].presence || + titles['english'].presence || + titles['native'].presence || + titles['userPreferred'] end def anilist_mapping diff --git a/lib/anilist_api_wrapper.rb b/lib/anilist_api_wrapper.rb deleted file mode 100644 index 1481e8ead6..0000000000 --- a/lib/anilist_api_wrapper.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'graphql/client' -require 'graphql/client/http' - -module AnilistApiWrapper - GRAPHQL_API = 'https://graphql.anilist.co' - - begin - HTTP = GraphQL::Client::HTTP.new(GRAPHQL_API) do - def headers(_) - { 'Content-Type': 'application/json' } - end - end - - Schema = GraphQL::Client.load_schema(HTTP) - - Client = GraphQL::Client.new(schema: Schema, execute: HTTP) - rescue StandardError => e - Sentry.capture_exception(e) - end -end diff --git a/spec/models/list_import/anilist_spec.rb b/spec/models/list_import/anilist_spec.rb index 2c27561ae6..0776fc71d5 100644 --- a/spec/models/list_import/anilist_spec.rb +++ b/spec/models/list_import/anilist_spec.rb @@ -10,15 +10,9 @@ ) end - let(:media_lists) do - JSON.parse(fixture('list_import/anilist/toyhammered_full_list.json')) - .deep_transform_keys(&:underscore) - end - before do - allow(AnilistApiWrapper::Client).to receive(:query) { - JSON.parse(media_lists.to_json, object_class: OpenStruct) - } + stub_request(:post, 'https://graphql.anilist.co') + .to_return(body: fixture('list_import/anilist/toyhammered_full_list.json')) end describe 'validations' do