Skip to content

Commit

Permalink
Replace swagger-based QA client with simple fetch calls
Browse files Browse the repository at this point in the history
Fixes #3262

This commit replaces the swagger-based QA client. Why?

1. the API specification is incomplete;
2. it's not clear there is a plan in place to keep the specification up to date; and
3. we only use a fraction of the QA API in the Sinopia editor

In short, it's to pay down technical debt. This commit otherwise retains the same interface for interacting with the QA API, returning a promise that resolves to the HTTP response.
  • Loading branch information
mjgiarlo authored and justinlittman committed Nov 17, 2021
1 parent 6807a1f commit b3553a0
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 2,301 deletions.
135 changes: 70 additions & 65 deletions __tests__/actionCreators/search.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import {
fetchQASearchResults,
} from "actionCreators/search"
import * as server from "sinopiaSearch"
import Swagger from "swagger-client"
import configureMockStore from "redux-mock-store"
import thunk from "redux-thunk"
import { createState } from "stateUtils"
import * as sinopiaApi from "sinopiaApi"

jest.mock("swagger-client")
import * as QuestioningAuthority from "utilities/QuestioningAuthority"

const mockStore = configureMockStore([thunk])

Expand Down Expand Up @@ -88,7 +86,8 @@ describe("fetchSinopiaSearchResults", () => {
describe("fetchQASearchResults", () => {
const query = "*"
const uri = "urn:ld4p:qa:sharevde_stanford_ld4l_cache:all"
it("dispatches action", async () => {

describe("when happy path", () => {
const mockSearchResults = [
{
uri: "http://share-vde.org/sharevde/rdfBibframe/Work/3107365",
Expand Down Expand Up @@ -147,77 +146,83 @@ describe("fetchQASearchResults", () => {
],
},
]
const mockActionFunction = jest.fn().mockResolvedValue({
body: {
results: mockSearchResults,
response_header: { total_records: 15 },
},
})
const client = {
apis: { SearchQuery: { GET_searchAuthority: mockActionFunction } },
const mockResponse = {
results: mockSearchResults,
response_header: { total_records: 15 },
}
Swagger.mockResolvedValue(client)
sinopiaApi.putUserHistory = jest.fn().mockResolvedValue()

const store = mockStore(createState())
await store.dispatch(fetchQASearchResults(query, uri, "testerrorkey"))
beforeEach(() => {
jest
.spyOn(QuestioningAuthority, "createLookupPromise")
.mockResolvedValue(mockResponse)
})

const actions = store.getActions()
it("dispatches action", async () => {
sinopiaApi.putUserHistory = jest.fn().mockResolvedValue()

expect(actions).toHaveLength(3)
expect(actions).toHaveAction("CLEAR_ERRORS")
expect(actions).toHaveAction("SET_SEARCH_RESULTS", {
searchType: "resource",
uri,
query,
results: mockSearchResults,
totalResults: 15,
options: {},
error: undefined,
facetResults: {},
})
expect(actions).toHaveAction("ADD_SEARCH_HISTORY", {
authorityUri: uri,
authorityLabel: "SHAREVDE STANFORD (QA)",
query,
const store = mockStore(createState())
await store.dispatch(fetchQASearchResults(query, uri, "testerrorkey"))

const actions = store.getActions()

expect(actions).toHaveLength(3)
expect(actions).toHaveAction("CLEAR_ERRORS")
expect(actions).toHaveAction("SET_SEARCH_RESULTS", {
searchType: "resource",
uri,
query,
results: mockSearchResults,
totalResults: 15,
options: {},
error: undefined,
facetResults: {},
})
expect(actions).toHaveAction("ADD_SEARCH_HISTORY", {
authorityUri: uri,
authorityLabel: "SHAREVDE STANFORD (QA)",
query,
})
expect(sinopiaApi.putUserHistory).toHaveBeenCalledWith(
"Foo McBar",
"search",
"4682c287952df68172c6c4a63bdc2887",
'{"authorityUri":"urn:ld4p:qa:sharevde_stanford_ld4l_cache:all","query":"*"}'
)
})
expect(sinopiaApi.putUserHistory).toHaveBeenCalledWith(
"Foo McBar",
"search",
"4682c287952df68172c6c4a63bdc2887",
'{"authorityUri":"urn:ld4p:qa:sharevde_stanford_ld4l_cache:all","query":"*"}'
)
})

it("dispatches action when error", async () => {
const mockActionFunction = jest
.fn()
.mockRejectedValue(new Error("Ooops..."))
const client = {
apis: { SearchQuery: { GET_searchAuthority: mockActionFunction } },
}
Swagger.mockResolvedValue(client)
describe("when error occurs", () => {
beforeEach(() => {
jest
.spyOn(QuestioningAuthority, "createLookupPromise")
.mockResolvedValue({
isError: true,
errorObject: new Error("Ooops..."),
})
})

const store = mockStore(createState())
await store.dispatch(fetchQASearchResults(query, uri, "testerrorkey"))
it("dispatches action when error", async () => {
const store = mockStore(createState())
await store.dispatch(fetchQASearchResults(query, uri, "testerrorkey"))

const actions = store.getActions()
const actions = store.getActions()

expect(actions).toHaveLength(3)
expect(actions).toHaveAction("CLEAR_ERRORS")
expect(actions).toHaveAction("SET_SEARCH_RESULTS", {
searchType: "resource",
uri,
query,
results: [],
totalResults: 0,
options: {},
facetResults: {},
error: "Ooops...",
})
expect(actions).toHaveAction("ADD_ERROR", {
errorKey: "testerrorkey",
error: ["An error occurred while searching: Ooops..."],
expect(actions).toHaveLength(3)
expect(actions).toHaveAction("CLEAR_ERRORS")
expect(actions).toHaveAction("SET_SEARCH_RESULTS", {
searchType: "resource",
uri,
query,
results: [],
totalResults: 0,
options: {},
facetResults: {},
error: "Ooops...",
})
expect(actions).toHaveAction("ADD_ERROR", {
errorKey: "testerrorkey",
error: ["An error occurred while searching: Ooops..."],
})
})
})
})
87 changes: 42 additions & 45 deletions __tests__/components/search/Search.test.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,56 @@
import { renderApp } from "testUtils"
import { featureSetup } from "featureUtils"
import { fireEvent, waitFor, screen } from "@testing-library/react"
import * as server from "sinopiaSearch"
import Swagger from "swagger-client"
import Config from "Config"
import * as sinopiaApi from "sinopiaApi"
import * as QuestioningAuthority from "utilities/QuestioningAuthority"

jest.mock("swagger-client")
featureSetup()

describe("<Search />", () => {
jest.spyOn(sinopiaApi, "putUserHistory").mockResolvedValue()

it("requests a QA search", async () => {
const mockSearchResults = [
{
uri: "http://share-vde.org/sharevde/rdfBibframe/Work/3107365",
id: "http://share-vde.org/sharevde/rdfBibframe/Work/3107365",
label: "These twain",
context: [
{
property: "Title",
values: [" These twain"],
selectable: true,
drillable: false,
},
{
property: "Type",
values: [
"http://id.loc.gov/ontologies/bflc/Hub",
"http://id.loc.gov/ontologies/bibframe/Work",
],
selectable: false,
drillable: false,
},
{
property: "Contributor",
values: ["Bennett, Arnold,1867-1931."],
selectable: false,
drillable: false,
},
],
},
]
const mockActionFunction = jest.fn().mockResolvedValue({
body: {
results: mockSearchResults,
response_header: { total_records: 15 },
},
})
const client = {
apis: {
SearchQuery: { GET_nonldSearchWithSubauthority: mockActionFunction },
},
}
Swagger.mockResolvedValue(client)
const mockSearchResults = [
{
uri: "http://share-vde.org/sharevde/rdfBibframe/Work/3107365",
id: "http://share-vde.org/sharevde/rdfBibframe/Work/3107365",
label: "These twain",
context: [
{
property: "Title",
values: [" These twain"],
selectable: true,
drillable: false,
},
{
property: "Type",
values: [
"http://id.loc.gov/ontologies/bflc/Hub",
"http://id.loc.gov/ontologies/bibframe/Work",
],
selectable: false,
drillable: false,
},
{
property: "Contributor",
values: ["Bennett, Arnold,1867-1931."],
selectable: false,
drillable: false,
},
],
},
]
const mockResponse = {
results: mockSearchResults,
response_header: { total_records: 15 },
}

jest
.spyOn(QuestioningAuthority, "createLookupPromise")
.mockResolvedValue(mockResponse)

it("requests a QA search", async () => {
renderApp()
fireEvent.click(screen.getByText("Linked Data Editor", { selector: "a" }))

Expand Down
68 changes: 24 additions & 44 deletions __tests__/utilities/Lookup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,29 +135,19 @@ describe("getLookupResult()", () => {
describe("QA lookup", () => {
beforeEach(() => {
qaSearch.createLookupPromise.mockResolvedValue({
ok: true,
url: "https://lookup.ld4l.org/authorities/search/linked_data/agrovoc_ld4l_cache?q=Corn&maxRecords=8&lang=en&context=true",
status: 200,
statusText: "OK",
body: {
response_header: {
start_record: 1,
requested_records: 1,
retrieved_records: 1,
total_records: 1,
},
results: [
{
uri: "http://aims.fao.org/aos/agrovoc/c_331388",
id: "http://aims.fao.org/aos/agrovoc/c_331388",
label: "corn sheller",
},
],
response_header: {
start_record: 1,
requested_records: 1,
retrieved_records: 1,
total_records: 1,
},
authLabel: "AGROVOC (QA)",
authURI: "urn:ld4p:qa:agrovoc",
label: "AGROVOC (QA)",
id: "urn:ld4p:qa:agrovoc",
results: [
{
uri: "http://aims.fao.org/aos/agrovoc/c_331388",
id: "http://aims.fao.org/aos/agrovoc/c_331388",
label: "corn sheller",
},
],
})
})
it("returns result", async () => {
Expand All @@ -184,29 +174,19 @@ describe("getLookupResult()", () => {
})
describe("QA lookup with bad total_records", () => {
qaSearch.createLookupPromise = jest.fn().mockResolvedValue({
ok: true,
url: "https://lookup.ld4l.org/authorities/search/linked_data/agrovoc_ld4l_cache?q=Corn&maxRecords=8&lang=en&context=true",
status: 200,
statusText: "OK",
body: {
response_header: {
start_record: 1,
requested_records: 1,
retrieved_records: 1,
total_records: "NOT_REPORTED",
},
results: [
{
uri: "http://aims.fao.org/aos/agrovoc/c_331388",
id: "http://aims.fao.org/aos/agrovoc/c_331388",
label: "corn sheller",
},
],
response_header: {
start_record: 1,
requested_records: 1,
retrieved_records: 1,
total_records: "NOT_REPORTED",
},
authLabel: "AGROVOC (QA)",
authURI: "urn:ld4p:qa:agrovoc",
label: "AGROVOC (QA)",
id: "urn:ld4p:qa:agrovoc",
results: [
{
uri: "http://aims.fao.org/aos/agrovoc/c_331388",
id: "http://aims.fao.org/aos/agrovoc/c_331388",
label: "corn sheller",
},
],
})
it("returns result", async () => {
const authorityConfig = findAuthorityConfig("urn:ld4p:qa:agrovoc")
Expand Down
Loading

0 comments on commit b3553a0

Please sign in to comment.