diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index a37281ad5..13ecd597b 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -1848,6 +1848,292 @@ type DataCatalogEdge { node: DataCatalog } +type DataManagementPlan implements DoiItem { + """ + Metadata in bibtex format + """ + bibtex: String! + + """ + Total number of citations + """ + citationCount: Int + + """ + Citations for this DOI. + """ + citations(affiliationId: String, after: String, fieldOfScience: String, first: Int = 25, funderId: String, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, resourceTypeId: String, userId: String): WorkConnectionWithTotal + + """ + Citations by year + """ + citationsOverTime: [YearTotal!] + + """ + The container (e.g. journal or repository) hosting the resource. + """ + container: Container + + """ + The main researchers involved in producing the data, or the authors of the publication, in priority order + """ + creators(first: Int = 20): [Creator!] + + """ + Different dates relevant to the work + """ + dates: [Date!] + + """ + All additional information that does not fit in any of the other categories + """ + descriptions(first: Int = 5): [Description!] + + """ + The DOI for the resource. + """ + doi: String! + + """ + Total number of downloads + """ + downloadCount: Int + + """ + Downloads by month + """ + downloadsOverTime: [YearMonthTotal!] + + """ + OECD Fields of Science of the resource + """ + fieldsOfScience: [FieldOfScience!] + + """ + Technical format of the resource + """ + formats: [String!] + + """ + Metadata as formatted citation + """ + formattedCitation(locale: String = "en-US", style: String = "apa"): String + + """ + Information about financial support (funding) for the resource being registered + """ + fundingReferences: [Funding!] + + """ + Spatial region or named place where the data was gathered or about which the data is focused. + """ + geolocations: [Geolocation!] + + """ + The persistent identifier for the resource + """ + id: ID! + + """ + An identifier or identifiers applied to the resource being registered + """ + identifiers: [Identifier!] + + """ + The primary language of the resource + """ + language: Language + + """ + The member account managing this resource + """ + member: Member + + """ + Total number of parts + """ + partCount: Int + + """ + The DOI is a part of this DOI. + """ + partOf(affiliationId: String, after: String, fieldOfScience: String, first: Int = 25, funderId: String, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, resourceTypeId: String, userId: String): WorkConnectionWithTotal + + """ + Total number of DOIs the resource is a part of + """ + partOfCount: Int + + """ + Parts of this DOI. + """ + parts(affiliationId: String, after: String, fieldOfScience: String, first: Int = 25, funderId: String, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, resourceTypeId: String, userId: String): WorkConnectionWithTotal + + """ + The year when the data was or will be made publicly available + """ + publicationYear: Int + + """ + The name of the entity that holds, archives, publishes prints, distributes, releases, issues, or produces the resource + """ + publisher: String + + """ + Total number of references + """ + referenceCount: Int + + """ + References for this DOI + """ + references(affiliationId: String, after: String, fieldOfScience: String, first: Int = 25, funderId: String, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, resourceTypeId: String, userId: String): WorkConnectionWithTotal + + """ + DOI registration date + """ + registered: ISO8601DateTime + + """ + The DOI registration agency for the resource + """ + registrationAgency: RegistrationAgency + + """ + Identifiers of related resources. These must be globally unique identifiers + """ + relatedIdentifiers: [RelatedIdentifier!] + + """ + The repository account managing this resource + """ + repository: Repository + + """ + Any rights information for this resource + """ + rights: [Rights!] + + """ + Metadata in schema.org format + """ + schemaOrg: JSON! + + """ + Size (e.g. bytes, pages, inches, etc.) or duration (extent), e.g. hours, minutes, days, etc., of a resource + """ + sizes: [String!] + + """ + Subject, keyword, classification code, or key phrase describing the resource + """ + subjects: [Subject!] + + """ + A name or title by which a resource is known + """ + titles(first: Int = 5): [Title!] + + """ + The type of the item. + """ + type: String! + + """ + The resource type + """ + types: ResourceType! + + """ + The URL registered for the resource + """ + url: Url + + """ + The version number of the resource + """ + version: String + + """ + Total number of versions + """ + versionCount: Int + + """ + The DOI is a version of this DOI. + """ + versionOf(affiliationId: String, after: String, fieldOfScience: String, first: Int = 25, funderId: String, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, resourceTypeId: String, userId: String): WorkConnectionWithTotal + + """ + Total number of DOIs the resource is a version of + """ + versionOfCount: Int + + """ + Versions of this DOI. + """ + versions(affiliationId: String, after: String, fieldOfScience: String, first: Int = 25, funderId: String, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, resourceTypeId: String, userId: String): WorkConnectionWithTotal + + """ + Total number of views + """ + viewCount: Int + + """ + Views by month + """ + viewsOverTime: [YearMonthTotal!] + + """ + Metadata in DataCite XML format. + """ + xml: String! +} + +""" +The connection type for DataManagementPlan. +""" +type DataManagementPlanConnectionWithTotal { + affiliations: [Facet!] + + """ + A list of edges. + """ + edges: [DataManagementPlanEdge] + fieldsOfScience: [Facet!] + languages: [Facet!] + licenses: [Facet!] + + """ + A list of nodes. + """ + nodes: [DataManagementPlan] + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + published: [Facet!] + registrationAgencies: [Facet!] + repositories: [Facet!] + totalCount: Int! +} + +""" +An edge in a connection. +""" +type DataManagementPlanEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of the edge. + """ + node: DataManagementPlan +} + type DataPaper implements DoiItem { """ Metadata in bibtex format @@ -7466,6 +7752,8 @@ type Query { conferencePapers(after: String, fieldOfScience: String, first: Int = 25, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, userId: String): ConferencePaperConnectionWithTotal! dataCatalog(id: ID!): DataCatalog! dataCatalogs(after: String, first: Int = 25, query: String): DataCatalogConnectionWithTotal! + dataManagementPlan(id: ID!): DataManagementPlan! + dataManagementPlans(after: String, fieldOfScience: String, first: Int = 25, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, userId: String): DataManagementPlanConnectionWithTotal! dataPaper(id: ID!): DataPaper! dataPapers(after: String, fieldOfScience: String, first: Int = 25, hasCitations: Int, hasDownloads: Int, hasFunder: Boolean, hasOrganization: Boolean, hasParts: Int, hasPerson: Boolean, hasVersions: Int, hasViews: Int, ids: [String!], language: String, license: String, memberId: String, published: String, query: String, registrationAgency: String, repositoryId: String, userId: String): DataPaperConnectionWithTotal! dataset(id: ID!): Dataset! diff --git a/app/graphql/types/data_management_plan_connection_with_total_type.rb b/app/graphql/types/data_management_plan_connection_with_total_type.rb new file mode 100644 index 000000000..d264295f8 --- /dev/null +++ b/app/graphql/types/data_management_plan_connection_with_total_type.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class DataManagementPlanConnectionWithTotalType < BaseConnection + edge_type(DataManagementPlanEdgeType) + field_class GraphQL::Cache::Field + + field :total_count, Integer, null: false, cache: true + field :published, [FacetType], null: true, cache: true + field :registration_agencies, [FacetType], null: true, cache: true + field :repositories, [FacetType], null: true, cache: true + field :affiliations, [FacetType], null: true, cache: true + field :fields_of_science, [FacetType], null: true, cache: true + field :licenses, [FacetType], null: true, cache: true + field :languages, [FacetType], null: true, cache: true + + def total_count + object.total_count + end + + def published + facet_by_range(object.aggregations.published.buckets) + end + + def registration_agencies + facet_by_registration_agency(object.aggregations.registration_agencies.buckets) + end + + def repositories + facet_by_combined_key(object.aggregations.clients.buckets) + end + + def affiliations + facet_by_combined_key(object.aggregations.affiliations.buckets) + end + + def fields_of_science + facet_by_fos(object.aggregations.fields_of_science.subject.buckets) + end + + def licenses + facet_by_license(object.aggregations.licenses.buckets) + end + + def languages + facet_by_language(object.aggregations.languages.buckets) + end +end diff --git a/app/graphql/types/data_management_plan_edge_type.rb b/app/graphql/types/data_management_plan_edge_type.rb new file mode 100644 index 000000000..2a3ab7ae9 --- /dev/null +++ b/app/graphql/types/data_management_plan_edge_type.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class DataManagementPlanEdgeType < GraphQL::Types::Relay::BaseEdge + node_type(DataManagementPlanType) +end diff --git a/app/graphql/types/data_management_plan_type.rb b/app/graphql/types/data_management_plan_type.rb new file mode 100644 index 000000000..332eaa41d --- /dev/null +++ b/app/graphql/types/data_management_plan_type.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class DataManagementPlanType < BaseObject + implements DoiItem + + def type + "DataManagementPlan" + end +end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 456d84872..095d8847b 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -767,6 +767,43 @@ def dissertation(id:) set_doi(id) end + field :data_management_plans, DataManagementPlanConnectionWithTotalType, null: false do + argument :query, String, required: false + argument :ids, [String], required: false + argument :published, String, required: false + argument :user_id, String, required: false + argument :repository_id, String, required: false + argument :member_id, String, required: false + argument :registration_agency, String, required: false + argument :license, String, required: false + argument :language, String, required: false + argument :has_person, Boolean, required: false + argument :has_funder, Boolean, required: false + argument :has_organization, Boolean, required: false + argument :has_citations, Int, required: false + argument :has_parts, Int, required: false + argument :has_versions, Int, required: false + argument :has_views, Int, required: false + argument :has_downloads, Int, required: false + argument :field_of_science, String, required: false + argument :first, Int, required: false, default_value: 25 + argument :after, String, required: false + end + + def data_management_plans(**args) + args[:resource_type_id] = "Text" + args[:resource_type] = "Data Management Plan" + ElasticsearchModelResponseConnection.new(response(args), context: self.context, first: args[:first], after: args[:after]) + end + + field :data_management_plan, DataManagementPlanType, null: false do + argument :id, ID, required: true + end + + def data_management_plan(id:) + set_doi(id) + end + field :preprints, PreprintConnectionWithTotalType, null: false do argument :query, String, required: false argument :ids, [String], required: false diff --git a/spec/graphql/types/data_management_plan_type_spec.rb b/spec/graphql/types/data_management_plan_type_spec.rb new file mode 100644 index 000000000..5286f0640 --- /dev/null +++ b/spec/graphql/types/data_management_plan_type_spec.rb @@ -0,0 +1,223 @@ +require "rails_helper" + +describe DataManagementPlanType do + describe "fields" do + subject { described_class } + + it { is_expected.to have_field(:id).of_type(!types.ID) } + it { is_expected.to have_field(:type).of_type("String!") } + end + + describe "query data_management_plans", elasticsearch: true do + let!(:data_management_plans) { create_list(:doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Data Management Plan" }, language: "de", aasm_state: "findable") } + + before do + Doi.import + sleep 2 + @dois = Doi.gql_query(nil, page: { cursor: [], size: 4 }).results.to_a + end + + let(:query) do + %(query { + dataManagementPlans { + totalCount + registrationAgencies { + id + title + count + } + licenses { + id + title + count + } + languages { + id + title + count + } + nodes { + id + registrationAgency { + id + name + } + } + } + }) + end + + it "returns all data_management_plans" do + response = LupoSchema.execute(query).as_json + + expect(response.dig("data", "dataManagementPlans", "totalCount")).to eq(2) + expect(response.dig("data", "dataManagementPlans", "languages")).to eq([{"count"=>2, "id"=>"de", "title"=>"German"}]) + expect(response.dig("data", "dataManagementPlans", "licenses")).to eq([{"count"=>2, "id"=>"cc0-1.0", "title"=>"CC0-1.0"}]) + expect(response.dig("data", "dataManagementPlans", "nodes").length).to eq(2) + expect(response.dig("data", "dataManagementPlans", "nodes", 0, "registrationAgency")).to eq("id"=>"datacite", "name"=>"DataCite") + end + end + + describe "query data_management_plans by language", elasticsearch: true do + let!(:data_management_plans) { create_list(:doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Data Management Plan" }, language: "de", aasm_state: "findable") } + + before do + Doi.import + sleep 2 + @dois = Doi.gql_query(nil, page: { cursor: [], size: 4 }).results.to_a + end + + let(:query) do + %(query { + dataManagementPlans(language: "de") { + totalCount + registrationAgencies { + id + title + count + } + licenses { + id + title + count + } + languages { + id + title + count + } + nodes { + id + rights { + rights + rightsUri + rightsIdentifier + } + language { + id + name + } + registrationAgency { + id + name + } + } + } + }) + end + + it "returns all data_management_plans" do + response = LupoSchema.execute(query).as_json + + expect(response.dig("data", "dataManagementPlans", "totalCount")).to eq(2) + expect(response.dig("data", "dataManagementPlans", "registrationAgencies")).to eq([{"count"=>2, "id"=>"datacite", "title"=>"DataCite"}]) + expect(response.dig("data", "dataManagementPlans", "licenses")).to eq([{"count"=>2, "id"=>"cc0-1.0", "title"=>"CC0-1.0"}]) + expect(response.dig("data", "dataManagementPlans", "nodes").length).to eq(2) + expect(response.dig("data", "dataManagementPlans", "nodes", 0, "rights")).to eq([{"rights"=>"Creative Commons Zero v1.0 Universal", + "rightsIdentifier"=>"cc0-1.0", + "rightsUri"=>"https://creativecommons.org/publicdomain/zero/1.0/legalcode"}]) + expect(response.dig("data", "dataManagementPlans", "nodes", 0, "registrationAgency")).to eq("id"=>"datacite", "name"=>"DataCite") + end + end + + describe "query data_management_plans by license", elasticsearch: true do + let!(:data_management_plans) { create_list(:doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Data Management Plan" }, language: "de", aasm_state: "findable") } + + before do + Doi.import + sleep 2 + @dois = Doi.gql_query(nil, page: { cursor: [], size: 4 }).results.to_a + end + + let(:query) do + %(query { + dataManagementPlans(license: "cc0-1.0") { + totalCount + registrationAgencies { + id + title + count + } + languages { + id + title + count + } + licenses { + id + title + count + } + nodes { + id + registrationAgency { + id + name + } + language { + id + name + } + rights { + rights + rightsUri + rightsIdentifier + } + } + } + }) + end + + it "returns all data_management_plans" do + response = LupoSchema.execute(query).as_json + + expect(response.dig("data", "dataManagementPlans", "totalCount")).to eq(2) + expect(response.dig("data", "dataManagementPlans", "registrationAgencies")).to eq([{"count"=>2, "id"=>"datacite", "title"=>"DataCite"}]) + expect(response.dig("data", "dataManagementPlans", "languages")).to eq([{"count"=>2, "id"=>"de", "title"=>"German"}]) + expect(response.dig("data", "dataManagementPlans", "nodes").length).to eq(2) + expect(response.dig("data", "dataManagementPlans", "nodes", 0, "registrationAgency")).to eq("id"=>"datacite", "name"=>"DataCite") + end + end + + describe "query data_management_plans by person", elasticsearch: true do + let!(:data_management_plans) { create_list(:doi, 3, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Data Management Plan" }, aasm_state: "findable") } + let!(:data_management_plan) { create(:doi, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Data Management Plan" }, aasm_state: "findable", creators: + [{ + "familyName" => "Garza", + "givenName" => "Kristian", + "name" => "Garza, Kristian", + "nameIdentifiers" => [{"nameIdentifier"=>"https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme"=>"ORCID", "schemeUri"=>"https://orcid.org"}], + "nameType" => "Personal", + }]) + } + before do + Doi.import + sleep 2 + @dois = Doi.gql_query(nil, page: { cursor: [], size: 4 }).results.to_a + end + + let(:query) do + %(query { + dataManagementPlans(userId: "https://orcid.org/0000-0003-1419-2405") { + totalCount + published { + id + title + count + } + nodes { + id + } + } + }) + end + + it "returns data_management_plans" do + response = LupoSchema.execute(query).as_json + + expect(response.dig("data", "dataManagementPlans", "totalCount")).to eq(3) + expect(response.dig("data", "dataManagementPlans", "published")).to eq([{"count"=>3, "id"=>"2011", "title"=>"2011"}]) + expect(response.dig("data", "dataManagementPlans", "nodes").length).to eq(3) + end + end +end