diff --git a/cypress/integration/01_metastore.spec.js b/cypress/integration/01_metastore.spec.js index 5e7ae56d4e..1e2ba6d3d2 100644 --- a/cypress/integration/01_metastore.spec.js +++ b/cypress/integration/01_metastore.spec.js @@ -1,12 +1,182 @@ -import * as dkan from '../support/helpers/dkan' -const api_uri = Cypress.config('apiUri') -const user_credentials = Cypress.env('TEST_USER_CREDENTIALS') -const uuid_regex = new RegExp(Cypress.env('UUID_REGEX')) - context('Metastore', () => { + + const api_uri = Cypress.config('apiUri') + const user_credentials = Cypress.env('TEST_USER_CREDENTIALS') + const uuid_regex = new RegExp(Cypress.env('UUID_REGEX')) + const metastore_schemas = [ + 'dataset', + 'publisher', + 'distribution', + 'theme', + 'keyword', + 'data-dictionary', + ] + + function getMetastoreCreateEndpoint (schema_id) { + return `/${api_uri}/metastore/schemas/${schema_id}/items` + } + + function getMetastoreGetEndpoint (schema_id, identifier) { + return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` + } + + function getMetastorePutEndpoint (schema_id, identifier) { + return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` + } + + function getMetastorePatchEndpoint (schema_id, identifier) { + return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` + } + + function getMetastoreDeleteEndpoint (schema_id, identifier) { + return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` + } + + // Generate a random uuid. + // Credit: https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript + function generateMetastoreIdentifier () { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8) + return v.toString(16) + }) + } + + function generateRandomString () { + return generateMetastoreIdentifier() + } + + function generateRandomDateString () { + const start = new Date('1970-01-01T00:00:00.000Z') + const end = new Date() + const date = new Date(+start + Math.random() * (end - start)) + + const year = date.getFullYear().toString() + const month = date.getMonth().toString().padStart(2, '0') + const day = date.getDate().toString().padStart(2, '0') + + return year + '-' + month + '-' + day + } + + // Create a metastore item via API. + function createMetastore (schema_id, item = null) { + item = item || generateMetastore(schema_id) + // Lookup the proper metastore creation procedure for the given schema ID. + return cy.request({ + method: 'POST', + url: `${api_uri}/metastore/schemas/${schema_id}/items`, + auth: user_credentials, + body: item + }) + } + + function generateMetastore (schema_id, identifier = null) { + // Generate a unique metastore identifier if one was not supplied. + identifier = identifier || generateMetastoreIdentifier() + // Lookup the proper metastore generation procedure for the given schema ID. + const metastore_generator_dictionary = { + "dataset": generateDataset, + "publisher": generatePublisher, + "distribution": generateDistribution, + "theme": generateTheme, + "keyword": generateKeyword, + "data-dictionary": generateDataDictionary, + } + return metastore_generator_dictionary[schema_id](identifier) + } + + // Generate a metastore dataset item object. + function generateDataset(uuid) { + return { + title: "Title for " + uuid, + description: "Description for " + uuid, + identifier: uuid, + accessLevel: "public", + bureauCode: ["1234:56"], + modified: generateRandomDateString(), + "@type": "dcat:Dataset", + distribution: [ + { + "@type": "dcat:Distribution", + downloadURL: "https://dkan-default-content-files.s3.amazonaws.com/phpunit/district_centerpoints_small.csv", + mediaType: "text/csv", + format: "csv", + description: `

${generateRandomString()}

`, + title: generateRandomString() + } + ], + keyword: [ + generateRandomString(), + generateRandomString(), + generateRandomString() + ], + contactPoint: { + "@type": "vcard:Contact", + fn: generateRandomString() + " " + generateRandomString(), + hasEmail: "mailto:first.last@example.com" + } + } + } + + // Generate a metastore publisher item object. + function generatePublisher(uuid) { + return { + "identifier": uuid, + "data": { + "@type": "org:Organization", + "name": generateRandomString(), + "subOrganizationOf": generateRandomString() + } + } + } + + function generateDistribution(uuid) { + return { + "identifier": uuid, + "data": { + "title": "Title for " + uuid, + "description": `

${generateRandomString()}

`, + "format": "csv", + "mediaType": "text/csv", + "downloadURL": "https://dkan-default-content-files.s3.amazonaws.com/phpunit/district_centerpoints_small.csv", + } + } + } + + function generateTheme(uuid) { + return { + "identifier": uuid, + "data": generateRandomString() + } + } + + function generateKeyword(uuid) { + return { + "identifier": uuid, + "data": generateRandomString() + } + } + + // Generate a metastore data-dictionary item object. + function generateDataDictionary(uuid) { + return { + "identifier": uuid, + "title": "Title for " + uuid, + "data": { + "fields": [ + { + "name": generateRandomString(), + "title": generateRandomString(), + "type": "string", + "format": "default" + } + ] + } + } + } + context('Catalog', () => { it('Should contain newly created datasets', () => { - dkan.createMetastore('dataset').then((response) => { + createMetastore('dataset').then((response) => { expect(response.status).eql(201) const identifier = response.body.identifier cy.request('data.json').then((response) => { @@ -36,27 +206,27 @@ context('Metastore', () => { const schema_id = 'dataset' it('bad query parameter', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { - cy.request(dkan.getMetastoreGetEndpoint(schema_id, response.body.identifier) + '?view=foobar').then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, response.body.identifier) + '?view=foobar').then((response) => { expect(response.body.keyword).eql(item.keyword) }) }) }) it('data (default)', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.body.keyword).eql(item.keyword) }) }) }) it('data+identifier', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier) + '?show-reference-ids').then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier) + '?show-reference-ids').then((response) => { expect(response.body.keyword).not.eql(item.keyword) expect(response.body.keyword.length).eql(item.keyword.length) expect(response.body.keyword[0].identifier).to.match(uuid_regex) @@ -70,10 +240,10 @@ context('Metastore', () => { }) }) - dkan.metastore_schemas.forEach((schema_id) => { + metastore_schemas.forEach((schema_id) => { context(`Metastore Item Creation (${schema_id})`, () => { it(`Create a ${schema_id}`, () => { - dkan.createMetastore(schema_id).then((response) => { + createMetastore(schema_id).then((response) => { expect(response.status).eql(201) expect(response.body.identifier).to.match(uuid_regex) }) @@ -82,7 +252,7 @@ context('Metastore', () => { it('Create request fails with an empty payload', () => { cy.request({ method: 'POST', - url: dkan.getMetastoreCreateEndpoint(schema_id), + url: getMetastoreCreateEndpoint(schema_id), auth: user_credentials, body: {}, failOnStatusCode: false @@ -94,7 +264,7 @@ context('Metastore', () => { it('Create request fails with no payload', () => { cy.request({ method: 'POST', - url: dkan.getMetastoreCreateEndpoint(schema_id), + url: getMetastoreCreateEndpoint(schema_id), auth: user_credentials, failOnStatusCode: false }).then((response) => { @@ -106,7 +276,7 @@ context('Metastore', () => { context(`Metastore Item Retrieval (${schema_id})`, () => { it(`GET a non-existent ${schema_id}`, () => { cy.request({ - url: dkan.getMetastoreGetEndpoint(schema_id, dkan.generateRandomString()), + url: getMetastoreGetEndpoint(schema_id, generateRandomString()), failOnStatusCode: false }).then((response) => { expect(response.status).eql(404) @@ -118,7 +288,7 @@ context('Metastore', () => { it('PUT fails with an empty payload', () => { cy.request({ method: 'PUT', - url: dkan.getMetastorePutEndpoint(schema_id, dkan.generateRandomString()), + url: getMetastorePutEndpoint(schema_id, generateRandomString()), auth: user_credentials, body: {}, failOnStatusCode: false @@ -130,7 +300,7 @@ context('Metastore', () => { it('PUT fails with no payload', () => { cy.request({ method: 'PUT', - url: dkan.getMetastorePutEndpoint(schema_id, dkan.generateRandomString()), + url: getMetastorePutEndpoint(schema_id, generateRandomString()), auth: user_credentials, failOnStatusCode: false }).then((response) => { @@ -139,13 +309,13 @@ context('Metastore', () => { }) it('PUT fails to modify the identifier', () => { - dkan.createMetastore(schema_id).then((response) => { + createMetastore(schema_id).then((response) => { expect(response.status).eql(201) cy.request({ method: 'PUT', - url: dkan.getMetastorePutEndpoint(schema_id, response.body.identifier), + url: getMetastorePutEndpoint(schema_id, response.body.identifier), auth: user_credentials, - body: dkan.generateMetastore(schema_id), + body: generateMetastore(schema_id), failOnStatusCode: false }).then((response) => { expect(response.status).eql(409) @@ -154,20 +324,20 @@ context('Metastore', () => { }) it('PUT updates an existing dataset', () => { - dkan.createMetastore(schema_id).then((response) => { + createMetastore(schema_id).then((response) => { expect(response.status).eql(201) - const new_item = dkan.generateMetastore(schema_id, response.body.identifier) + const new_item = generateMetastore(schema_id, response.body.identifier) cy.request({ method: 'PUT', - url: dkan.getMetastorePutEndpoint(schema_id, response.body.identifier), + url: getMetastorePutEndpoint(schema_id, response.body.identifier), auth: user_credentials, body: new_item }).then((response) => { expect(response.status).eql(200) - expect(response.body.endpoint).eql(dkan.getMetastoreGetEndpoint(schema_id, new_item.identifier)) + expect(response.body.endpoint).eql(getMetastoreGetEndpoint(schema_id, new_item.identifier)) expect(response.body.identifier).eql(response.body.identifier) // Verify item. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, response.body.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, response.body.identifier)).then((response) => { expect(response.status).eql(200) expect(response.body).eql(new_item) }) @@ -176,8 +346,8 @@ context('Metastore', () => { }) it('PUT creates a dataset, if non-existent', () => { - const item = dkan.generateMetastore(schema_id) - const endpoint = dkan.getMetastorePutEndpoint(schema_id, item.identifier) + const item = generateMetastore(schema_id) + const endpoint = getMetastorePutEndpoint(schema_id, item.identifier) cy.request({ method: 'PUT', url: endpoint, @@ -187,7 +357,7 @@ context('Metastore', () => { expect(response.status).eql(201) expect(response.body.endpoint).eql(endpoint) // Verify item. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.status).eql(200) expect(response.body).eql(item) }) @@ -195,18 +365,18 @@ context('Metastore', () => { }) it('PUT fails to modify if data is the same', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { cy.request({ method: 'PUT', - url: dkan.getMetastorePutEndpoint(schema_id, item.identifier), + url: getMetastorePutEndpoint(schema_id, item.identifier), auth: user_credentials, body: item, failOnStatusCode: false }).then((response) => { expect(response.status).eql(403) // Verify data has not been modified. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.status).eql(200) expect(response.body).eql(item) }) @@ -217,12 +387,12 @@ context('Metastore', () => { context(`Metastore Item Replacement (${schema_id})`, () => { it('PATCH fails for non-existent dataset', () => { - const identifier = dkan.generateRandomString(schema_id) + const identifier = generateRandomString(schema_id) cy.request({ method: 'PATCH', - url: dkan.getMetastorePatchEndpoint(schema_id, identifier), + url: getMetastorePatchEndpoint(schema_id, identifier), auth: user_credentials, - body: dkan.generateMetastore(schema_id, identifier), + body: generateMetastore(schema_id, identifier), failOnStatusCode: false }).then((response) => { expect(response.status).eql(412) @@ -230,14 +400,14 @@ context('Metastore', () => { }) it('PATCH fails to modify the identifier', () => { - dkan.createMetastore(schema_id).then((response) => { + createMetastore(schema_id).then((response) => { expect(response.status).eql(201) cy.request({ method: 'PATCH', - url: dkan.getMetastorePatchEndpoint(schema_id, response.body.identifier), + url: getMetastorePatchEndpoint(schema_id, response.body.identifier), auth: user_credentials, body: { - identifier: dkan.generateRandomString(schema_id) + identifier: generateRandomString(schema_id) }, failOnStatusCode: false }).then((response) => { @@ -247,19 +417,19 @@ context('Metastore', () => { }) it('PATCH - empty payload', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { expect(response.status).eql(201) cy.request({ method: 'PATCH', - url: dkan.getMetastorePatchEndpoint(schema_id, item.identifier), + url: getMetastorePatchEndpoint(schema_id, item.identifier), auth: user_credentials, body: {}, failOnStatusCode: false }).then((response) => { expect(response.status).eql(200) // Verify data. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.status).eql(200) expect(response.body).eql(item) }) @@ -268,18 +438,18 @@ context('Metastore', () => { }) it('PATCH - basic case', () => { - dkan.createMetastore(schema_id).then((response) => { + createMetastore(schema_id).then((response) => { expect(response.status).eql(201) - const item = dkan.generateMetastore(schema_id, response.body.identifier) + const item = generateMetastore(schema_id, response.body.identifier) cy.request({ method: 'PATCH', - url: dkan.getMetastorePatchEndpoint(schema_id, response.body.identifier), + url: getMetastorePatchEndpoint(schema_id, response.body.identifier), auth: user_credentials, body: item }).then((response) => { expect(response.status).eql(200) // Verify item. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.status).eql(200) expect(response.body).eql(item) }) @@ -290,16 +460,16 @@ context('Metastore', () => { context(`Metastore Item Deletion (${schema_id})`, () => { it('Delete existing datasets', () => { - dkan.createMetastore(schema_id).then((response) => { + createMetastore(schema_id).then((response) => { const identifier = response.body.identifier cy.request({ method: 'DELETE', - url: dkan.getMetastoreDeleteEndpoint(schema_id, identifier), + url: getMetastoreDeleteEndpoint(schema_id, identifier), auth: user_credentials }).then((response) => { expect(response.status).eql(200) cy.request({ - url: dkan.getMetastoreGetEndpoint(schema_id, identifier), + url: getMetastoreGetEndpoint(schema_id, identifier), failOnStatusCode: false }).then((response) => { expect(response.status).eql(404) @@ -313,16 +483,16 @@ context('Metastore', () => { context('Metastore Item Replacement (dataset; edge cases)', () => { const schema_id = 'dataset' it('PATCH modifies array elements (add, remove, edit)', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { expect(response.status).eql(201) const new_keywords = [ - dkan.generateRandomString(), - dkan.generateRandomString() + generateRandomString(), + generateRandomString() ] cy.request({ method: 'PATCH', - url: dkan.getMetastorePatchEndpoint(schema_id, item.identifier), + url: getMetastorePatchEndpoint(schema_id, item.identifier), auth: user_credentials, body: { keyword: new_keywords @@ -330,7 +500,7 @@ context('Metastore', () => { }).then((response) => { expect(response.status).eql(200) // Verify expected data: added, removed, edited and left unchanged. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.status).eql(200) new_keywords.forEach((keyword) => expect(response.body.keyword).to.contain(keyword)) item.keyword.forEach((keyword) => expect(response.body.keyword).to.not.contain(keyword)) @@ -340,14 +510,14 @@ context('Metastore', () => { }) it('PATCH modifies object properties (add, remove, edit)', () => { - const item = dkan.generateMetastore(schema_id) - dkan.createMetastore(schema_id, item).then((response) => { + const item = generateMetastore(schema_id) + createMetastore(schema_id, item).then((response) => { expect(response.status).eql(201) const new_keywords = [ - dkan.generateRandomString(), - dkan.generateRandomString() + generateRandomString(), + generateRandomString() ] - const endpoint = dkan.getMetastorePatchEndpoint(schema_id, item.identifier) + const endpoint = getMetastorePatchEndpoint(schema_id, item.identifier) cy.request({ method: 'PATCH', url: endpoint, @@ -364,7 +534,7 @@ context('Metastore', () => { expect(response.body.endpoint).eql(endpoint) expect(response.body.identifier).eql(item.identifier) // Verify expected data: added, removed, edited and left unchanged. - cy.request(dkan.getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { + cy.request(getMetastoreGetEndpoint(schema_id, item.identifier)).then((response) => { expect(response.status).eql(200) expect(response.body.contactPoint["fn"]).eql("Contact's name updated by PATCH") expect(response.body.contactPoint["@type"]).to.be.undefined diff --git a/cypress/integration/10_hidden_workflow_state.spec.js b/cypress/integration/10_hidden_workflow_state.spec.js deleted file mode 100644 index 6a43b76a79..0000000000 --- a/cypress/integration/10_hidden_workflow_state.spec.js +++ /dev/null @@ -1,90 +0,0 @@ -import * as dkan from '../support/helpers/dkan' - -const baseurl = Cypress.config().baseUrl; - -// create dataset -function createDataset(dataset_title, moderation_state) { - // Create a dataset. - cy.visit(baseurl + '/node/add/data') - cy.get('#edit-field-json-metadata-0-value-title') - .type(dataset_title, { force:true } ) - cy.get('#edit-field-json-metadata-0-value-description') - .type('DKANTEST dataset description.', { force:true } ) - cy.get('#edit-field-json-metadata-0-value-accesslevel') - .select('public', { force:true } ) - cy.get('#edit-field-json-metadata-0-value-modified-date') - .type('2020-02-02', { force:true } ) - // Fill select2 field for publisher. - cy.get('#edit-field-json-metadata-0-value-publisher-publisher-name + .select2') - .find('.select2-selection') - .click({ force:true }) - cy.get('input[aria-controls="select2-edit-field-json-metadata-0-value-publisher-publisher-name-results"]') - .type('DKANTEST Publisher{enter}') - // End filling up publisher. - cy.get('#edit-field-json-metadata-0-value-contactpoint-contactpoint-fn') - .type('DKANTEST Contact Name', { force:true } ) - cy.get('#edit-field-json-metadata-0-value-contactpoint-contactpoint-hasemail') - .type('dkantest@test.com', { force:true } ) - // Fill select2 field for keyword. - cy.get('#edit-field-json-metadata-0-value-keyword-keyword-0 + .select2') - .find('.select2-selection') - .click({ force: true }) - cy.get('input[aria-controls="select2-edit-field-json-metadata-0-value-keyword-keyword-0-results"]') - .type('open data{enter}') - cy.get('#edit-moderation-state-0-state') - .select(moderation_state, { force:true } ) - // End filling up keyword. - cy.get('#edit-submit') - .click({ force:true }) - cy.get('.messages--status') - .should('contain','has been created') -} - -context('Hidden datasets', () => { - beforeEach(() => { - cy.drupalLogin('testeditor', 'testeditor') - }) - - it('Newly created hidden datasets are visible when visited directly, but hidden from the catalog.', () => { - // create hidden dataset - const dataset_title = dkan.generateRandomString() - createDataset(dataset_title, 'hidden') - cy.visit(baseurl + '/admin/dkan/datasets') - cy.get('tbody > :nth-child(1) > .views-field-title > a').click() - cy.should('contain', dataset_title) - // ensure dataset is hidden from catalog - dkan.searchMetastore({fulltext: dataset_title, facets: ''}).then((response) => { - expect(response.status).to.eq(200) - expect(response.body.results).to.be.empty - }) - }) - - it('Existing datasets which are transitioned to hidden are visible when visited directly, but hidden from the catalog.', () => { - // create published dataset - const dataset_title = dkan.generateRandomString() - createDataset(dataset_title, 'published') - // ensure dataset is present in catalog - dkan.searchMetastore({fulltext: dataset_title, facets: ''}).then((response) => { - expect(response.status).to.eq(200) - expect(response.body.total).to.eq('1') - }) - - // hide the published dataset - cy.visit(baseurl + '/admin/dkan/datasets') - cy.get('tbody > :nth-child(1) > .views-field-nothing > a').click() - cy.get('#edit-moderation-state-0-state').select('hidden') - cy.get('#edit-submit').click() - cy.get('.messages--status').should('contain', 'has been updated') - // ensure dataset is hidden from catalog - dkan.searchMetastore({fulltext: dataset_title, facets: ''}).then((response) => { - expect(response.status).to.eq(200) - expect(response.body.results).to.be.empty - }) - - // ensure dataset details are available when directly visited - cy.visit(baseurl + '/admin/dkan/datasets') - cy.get('tbody > :nth-child(1) > .views-field-title > a').click() - cy.should('contain', dataset_title) - }) - -}) diff --git a/cypress/support/commands/drupal.js b/cypress/support/commands.js similarity index 74% rename from cypress/support/commands/drupal.js rename to cypress/support/commands.js index ed3cdd9131..624c05683a 100644 --- a/cypress/support/commands/drupal.js +++ b/cypress/support/commands.js @@ -1,5 +1,7 @@ -// login as a given drupal user -Cypress.Commands.add('drupalLogin', (user, password) => { +/** + * Drupal Collection + */ +Cypress.Commands.add("drupalLogin", (user, password) => { return cy.request({ method: 'POST', url: '/user/login', @@ -12,13 +14,11 @@ Cypress.Commands.add('drupalLogin', (user, password) => { }); }); -// logout of drupal Cypress.Commands.add('drupalLogout', () => { return cy.request('/user/logout'); }); -// Run the supplied drush command -Cypress.Commands.add('drupalDrushCommand', (command) => { +Cypress.Commands.add("drupalDrushCommand", (command) => { var cmd = Cypress.env('drupalDrushCmdLine'); if (cmd == null) { diff --git a/cypress/support/helpers/dkan.js b/cypress/support/helpers/dkan.js deleted file mode 100644 index f19cedaa21..0000000000 --- a/cypress/support/helpers/dkan.js +++ /dev/null @@ -1,186 +0,0 @@ -const api_uri = Cypress.config('apiUri') -const user_credentials = Cypress.env('TEST_USER_CREDENTIALS') - -export const metastore_schemas = [ - 'dataset', - 'publisher', - 'distribution', - 'theme', - 'keyword', - 'data-dictionary', -] - -export function getMetastoreCreateEndpoint (schema_id) { - return `/${api_uri}/metastore/schemas/${schema_id}/items` -} - -export function getMetastoreGetEndpoint (schema_id, identifier) { - return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` -} - -export function getMetastorePutEndpoint (schema_id, identifier) { - return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` -} - -export function getMetastorePatchEndpoint (schema_id, identifier) { - return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` -} - -export function getMetastoreDeleteEndpoint (schema_id, identifier) { - return `/${api_uri}/metastore/schemas/${schema_id}/items/${identifier}` -} - -function getMetastoreSearchEndpoint () { - return `/${api_uri}/search` -} - -// Generate a random uuid. -// Credit: https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript -export function generateMetastoreIdentifier () { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8) - return v.toString(16) - }) -} - -export function generateRandomString () { - return generateMetastoreIdentifier() -} - -export function generateRandomDateString () { - const start = new Date('1970-01-01T00:00:00.000Z') - const end = new Date() - const date = new Date(+start + Math.random() * (end - start)) - - const year = date.getFullYear().toString() - const month = date.getMonth().toString().padStart(2, '0') - const day = date.getDate().toString().padStart(2, '0') - - return year + '-' + month + '-' + day -} - -// Create a metastore item via API. -export function createMetastore (schema_id, item = null) { - item = item || generateMetastore(schema_id) - // Lookup the proper metastore creation procedure for the given schema ID. - return cy.request({ - method: 'POST', - url: getMetastoreCreateEndpoint(schema_id), - auth: user_credentials, - body: item - }) -} - -// search metastore -export function searchMetastore (params = {}) { - // build url - const param_string = (new URLSearchParams(params)).toString() - const url = getMetastoreSearchEndpoint() + '?' + param_string - // perform request - return cy.request('GET', url) -} - -export function generateMetastore (schema_id, identifier = null) { - // Generate a unique metastore identifier if one was not supplied. - identifier = identifier || generateMetastoreIdentifier() - // Lookup the proper metastore generation procedure for the given schema ID. - const metastore_generator_dictionary = { - "dataset": generateDataset, - "publisher": generatePublisher, - "distribution": generateDistribution, - "theme": generateTheme, - "keyword": generateKeyword, - "data-dictionary": generateDataDictionary, - } - return metastore_generator_dictionary[schema_id](identifier) -} - -// Generate a metastore dataset item object. -export function generateDataset(uuid) { - return { - title: "Title for " + uuid, - description: "Description for " + uuid, - identifier: uuid, - accessLevel: "public", - bureauCode: ["1234:56"], - modified: generateRandomDateString(), - "@type": "dcat:Dataset", - distribution: [ - { - "@type": "dcat:Distribution", - downloadURL: "https://dkan-default-content-files.s3.amazonaws.com/phpunit/district_centerpoints_small.csv", - mediaType: "text/csv", - format: "csv", - description: `

${generateRandomString()}

`, - title: generateRandomString() - } - ], - keyword: [ - generateRandomString(), - generateRandomString(), - generateRandomString() - ], - contactPoint: { - "@type": "vcard:Contact", - fn: generateRandomString() + " " + generateRandomString(), - hasEmail: "mailto:first.last@example.com" - } - } -} - -// Generate a metastore publisher item object. -export function generatePublisher(uuid) { - return { - "identifier": uuid, - "data": { - "@type": "org:Organization", - "name": generateRandomString(), - "subOrganizationOf": generateRandomString() - } - } -} - -export function generateDistribution(uuid) { - return { - "identifier": uuid, - "data": { - "title": "Title for " + uuid, - "description": `

${generateRandomString()}

`, - "format": "csv", - "mediaType": "text/csv", - "downloadURL": "https://dkan-default-content-files.s3.amazonaws.com/phpunit/district_centerpoints_small.csv", - } - } -} - -export function generateTheme(uuid) { - return { - "identifier": uuid, - "data": generateRandomString() - } -} - -export function generateKeyword(uuid) { - return { - "identifier": uuid, - "data": generateRandomString() - } -} - -// Generate a metastore data-dictionary item object. -export function generateDataDictionary(uuid) { - return { - "identifier": uuid, - "title": "Title for " + uuid, - "data": { - "fields": [ - { - "name": generateRandomString(), - "title": generateRandomString(), - "type": "string", - "format": "default" - } - ] - } - } -} diff --git a/cypress/support/index.js b/cypress/support/index.js index 4a68bb7ef2..d68db96df2 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -14,7 +14,7 @@ // *********************************************************** // Import commands.js using ES2015 syntax: -import './commands/drupal' +import './commands' // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/modules/datastore/src/Service/ResourcePurger.php b/modules/datastore/src/Service/ResourcePurger.php index 7d99eec29e..2a7bae9479 100644 --- a/modules/datastore/src/Service/ResourcePurger.php +++ b/modules/datastore/src/Service/ResourcePurger.php @@ -334,7 +334,7 @@ private function getRevisionData(string $vid) : array { private function getResources(NodeInterface $dataset) : array { $resources = []; $metadata = json_decode($dataset->get('field_json_metadata')->getString()); - $distributions = $metadata->{'%Ref:distribution'} ?? []; + $distributions = $metadata->{'%Ref:distribution'}; foreach ($distributions as $distribution) { // Retrieve and validate the resource for this distribution before adding diff --git a/modules/datastore/tests/src/Unit/Controller/MockStorage.php b/modules/datastore/tests/src/Unit/Controller/MockStorage.php index 580a8f9937..6f09e12957 100644 --- a/modules/datastore/tests/src/Unit/Controller/MockStorage.php +++ b/modules/datastore/tests/src/Unit/Controller/MockStorage.php @@ -18,7 +18,11 @@ public function retrieveByHash($hash, $schemaId) { return []; } - public function retrieve(string $uuid, bool $published = FALSE) : ?string { + public function retrievePublished(string $uuid) : ?string { + throw new MissingObjectException("Error retrieving published dataset: distribution {$uuid} not found."); + } + + public function retrieve(string $uuid) : ?string { throw new MissingObjectException("Error retrieving published dataset: distribution {$uuid} not found."); } diff --git a/modules/metastore/config/install/workflows.workflow.dkan_publishing.yml b/modules/metastore/config/install/workflows.workflow.dkan_publishing.yml index 8f5c12c7ff..522c26da53 100644 --- a/modules/metastore/config/install/workflows.workflow.dkan_publishing.yml +++ b/modules/metastore/config/install/workflows.workflow.dkan_publishing.yml @@ -1,4 +1,4 @@ -uuid: f13dbb18-99d0-41b2-b046-061b9dd5992d +uuid: c6063248-bbd1-4f27-bdd6-4bb7f60a50e3 langcode: en status: true dependencies: @@ -14,9 +14,9 @@ type: content_moderation type_settings: states: archived: - label: Archived published: false default_revision: true + label: Archived weight: 3 draft: label: Draft @@ -28,11 +28,6 @@ type_settings: default_revision: true label: Orphaned weight: 2 - hidden: - published: true - default_revision: true - label: 'Published (hidden)' - weight: 4 published: label: Published published: true @@ -42,7 +37,6 @@ type_settings: archive: label: Archive from: - - hidden - published to: archived weight: 3 @@ -52,13 +46,11 @@ type_settings: weight: 0 from: - draft - - hidden - published orphan: label: Orphan from: - draft - - hidden - published to: orphaned weight: 2 @@ -68,16 +60,7 @@ type_settings: weight: 1 from: - draft - - hidden - - published - hidden: - label: 'Remove from search indexing' - from: - - draft - - hidden - published - to: hidden - weight: 5 restore: label: Restore from: diff --git a/modules/metastore/metastore.install b/modules/metastore/metastore.install index 6a064573be..c311fdaeae 100644 --- a/modules/metastore/metastore.install +++ b/modules/metastore/metastore.install @@ -60,49 +60,3 @@ function metastore_update_8004() { drupal_flush_all_caches(); } - -/** - * Add hidden state and transitions to dkan publishing workflow config. - */ -function metastore_update_8005() { - $workflow_settings = \Drupal::service('config.factory')->getEditable('workflows.workflow.dkan_publishing'); - - $states = $workflow_settings->get('type_settings.states'); - $states['hidden'] = [ - 'label' => 'Published (hidden)', - 'published' => TRUE, - 'default_revision' => TRUE, - 'weight' => 4 - ]; - - $transitions = $workflow_settings->get('type_settings.transitions'); - $hidable = ['archive', 'create_new_draft', 'orphan', 'publish']; - foreach ($hidable as $state) { - $transitions[$state]['from'][] = 'hidden'; - } - $transitions['hidden'] = [ - 'label' => 'Remove from search indexing', - 'from' => [ - 'draft', - 'hidden', - 'published', - ], - 'to' => 'hidden', - 'weight' => 5, - ]; - - $workflow_settings->set('type_settings.states', $states); - $workflow_settings->set('type_settings.transitions', $transitions); - $workflow_settings->save(); -} - -/** - * Add hidden and unpublished filters to the dkan search_api index. - */ -function metastore_update_8006() { - $index_settings = \Drupal::service('config.factory')->getEditable('search_api.index.dkan'); - - $index_settings->set('processor_settings.dkan_dataset_filter_hidden', []); - $index_settings->set('processor_settings.dkan_dataset_filter_unpublished', []); - $index_settings->save(); -} diff --git a/modules/metastore/modules/metastore_search/config/install/search_api.index.dkan.yml b/modules/metastore/modules/metastore_search/config/install/search_api.index.dkan.yml index fa887572b3..ccf11e0a07 100644 --- a/modules/metastore/modules/metastore_search/config/install/search_api.index.dkan.yml +++ b/modules/metastore/modules/metastore_search/config/install/search_api.index.dkan.yml @@ -46,8 +46,6 @@ datasource_settings: processor_settings: add_url: { } aggregated_field: { } - dkan_dataset_filter_hidden: { } - dkan_dataset_filter_unpublished: { } ignorecase: all_fields: false fields: diff --git a/modules/metastore/modules/metastore_search/metastore_search.module b/modules/metastore/modules/metastore_search/metastore_search.module index ce48ebdbe4..646f7efcff 100644 --- a/modules/metastore/modules/metastore_search/metastore_search.module +++ b/modules/metastore/modules/metastore_search/metastore_search.module @@ -2,74 +2,57 @@ use Drupal\Core\Entity\EntityInterface; -use Drupal\common\Exception\DataNodeLifeCycleEntityValidationException; -use Drupal\metastore\NodeWrapper\Data; - /** - * Determine whether the supplied entity is a dataset. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity in question. - * - * @return bool - * Whether the entity is a dataset. + * Manage item tracking of search api items. */ -function _metastore_search_is_dataset(EntityInterface $entity): bool { - try { - // Attempt to check this entity's data type. - return (new Data($entity))->getDataType() === 'dataset'; +function metastore_search_search_api_tracking(EntityInterface $entity, string $operation) { + if ($entity->getEntityTypeId() != 'node') { + return; } - catch (DataNodeLifeCycleEntityValidationException $e) { - // If the data object fails validation, the given entity is not a dataset. - return FALSE; + + if ($entity->bundle() != 'data') { + return; } -} -/** - * Implements hook_entity_insert(). - * - * Track created datasets in the DKAN search api index. - */ -function metastore_search_entity_insert(EntityInterface $entity): void { - // Ensure the entity in question is a dataset and is not hidden or - // unpublished. - if (!_metastore_search_is_dataset($entity)) { + $wrapper = new Drupal\metastore\NodeWrapper\Data($entity); + if ($wrapper->getDataType() != 'dataset') { return; } - $storage = \Drupal::service('entity_type.manager')->getStorage('search_api_index'); + $acceptableOperation = ['Inserted', 'Updated', 'Deleted']; + + if (!in_array($operation, $acceptableOperation)) { + return; + } + + $method = "trackItems{$operation}"; + + $storage = \Drupal::service('entity_type.manager') + ->getStorage('search_api_index'); + + /* @var $index \Drupal\search_api\Entity\Index */ $index = $storage->load('dkan'); - $index->trackItemsInserted('dkan_dataset', [$entity->uuid()]); + + $index->{$method}('dkan_dataset', [$entity->uuid()]); } /** - * Implements hook_entity_update(). - * - * Track updated datasets in the DKAN search api index. + * Implements hook_entity_insert(). */ -function metastore_search_entity_update(EntityInterface $entity): void { - // Ensure the entity in question is a dataset. - if (!_metastore_search_is_dataset($entity)) { - return; - } +function metastore_search_entity_insert(EntityInterface $entity) { + metastore_search_search_api_tracking($entity, 'Inserted'); +} - $storage = \Drupal::service('entity_type.manager')->getStorage('search_api_index'); - $index = $storage->load('dkan'); - $index->trackItemsUpdated('dkan_dataset', [$entity->uuid()]); +/** + * Implements hook_entity_update(). + */ +function metastore_search_entity_update(EntityInterface $entity) { + metastore_search_search_api_tracking($entity, 'Updated'); } /** * Implements hook_entity_delete(). - * - * Track deleted datasets in the DKAN search api index. */ -function metastore_search_entity_delete(EntityInterface $entity): void { - // Ensure the entity in question is a dataset. - if (!_metastore_search_is_dataset($entity)) { - return; - } - - $storage = \Drupal::service('entity_type.manager')->getStorage('search_api_index'); - $index = $storage->load('dkan'); - $index->trackItemsDeleted('dkan_dataset', [$entity->uuid()]); +function metastore_search_entity_delete(EntityInterface $entity) { + metastore_search_search_api_tracking($entity, 'Deleted'); } diff --git a/modules/metastore/modules/metastore_search/src/Plugin/search_api/DkanDatasetFilterProcessorBase.php b/modules/metastore/modules/metastore_search/src/Plugin/search_api/DkanDatasetFilterProcessorBase.php deleted file mode 100644 index d9a5f6e0a3..0000000000 --- a/modules/metastore/modules/metastore_search/src/Plugin/search_api/DkanDatasetFilterProcessorBase.php +++ /dev/null @@ -1,81 +0,0 @@ -dataStorage = $configuration['dkan_data_storage']; - unset($configuration['dkan_data_storage']); - - parent::__construct($configuration, $plugin_id, $plugin_definition); - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - $dataStorageFactory = $container->get('dkan.metastore.storage'); - $configuration['dkan_data_storage'] = $dataStorageFactory->getInstance('dataset'); - - return parent::create($container, $configuration, $plugin_id, $plugin_definition); - } - - /** - * {@inheritdoc} - */ - public static function supportsIndex(IndexInterface $index) { - foreach ($index->getDatasources() as $datasource) { - $datasource_id = $datasource->getPluginId(); - // We only support indexes with the dkan_dataset datasource. - if ($datasource_id === 'dkan_dataset') { - return TRUE; - } - } - return FALSE; - } - - /** - * {@inheritdoc} - */ - public function alterIndexedItems(array &$items) { - foreach (array_keys($items) as $item_id) { - // Retrieve item object. - $item_object = $items[$item_id]->getOriginalObject(); - // Extract dataset ID. - $id_parts = Utility::splitCombinedId($item_id); - $dataset_id = $id_parts[1]; - - // Filter out invalid datasets. - if ($item_object instanceof Dataset && !$this->isValid($dataset_id)) { - unset($items[$item_id]); - } - } - } - - /** - * {@inheritdoc} - */ - abstract public function isValid(string $dataset_id): bool; - -} diff --git a/modules/metastore/modules/metastore_search/src/Plugin/search_api/DkanDatasetFilterProcessorInterface.php b/modules/metastore/modules/metastore_search/src/Plugin/search_api/DkanDatasetFilterProcessorInterface.php deleted file mode 100644 index 67f5221c8a..0000000000 --- a/modules/metastore/modules/metastore_search/src/Plugin/search_api/DkanDatasetFilterProcessorInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -accessCheck(FALSE) + ->condition('status', 1) ->condition('type', 'data') ->condition('field_data_type', 'dataset'); - if (isset($page)) { - $ids_query->range($page * self::PAGE_SIZE, self::PAGE_SIZE); - } + $total = $query->count()->execute(); + $pages = floor($total / $pageSize); - $uuids = array_map(function ($node) { - return $node->uuid(); - }, Node::loadMultiple($ids_query->execute())); + if ($page <= $pages) { - return $uuids ?: NULL; + $query = \Drupal::entityQuery('node') + ->accessCheck(FALSE) + ->condition('status', 1) + ->condition('type', 'data') + ->condition('field_data_type', 'dataset') + ->range($page * $pageSize, $pageSize); + $nids = $query->execute(); + + foreach ($nids as $id) { + $node = Node::load($id); + $ids[] = $node->uuid(); + } + + return $ids; + } + return NULL; } /** @@ -66,12 +74,18 @@ public function getItemIds($page = NULL) { public function loadMultiple(array $ids) { /* @var \Drupal\metastore\Storage\DataFactory $dataStorageFactory */ $dataStorageFactory = \Drupal::service("dkan.metastore.storage"); + /* @var \Drupal\metastore\Storage\Data $dataStorage */ $dataStorage = $dataStorageFactory->getInstance('dataset'); - $items = array_map(function ($id) use ($dataStorage) { - return new Dataset($dataStorage->retrieve($id, TRUE)); - }, array_combine($ids, $ids)); + $items = []; + foreach ($ids as $id) { + try { + $items[$id] = new Dataset($dataStorage->retrievePublished($id)); + } + catch (\Exception $e) { + } + } return $items; } diff --git a/modules/metastore/modules/metastore_search/src/Plugin/search_api/processor/DkanDatasetFilterHidden.php b/modules/metastore/modules/metastore_search/src/Plugin/search_api/processor/DkanDatasetFilterHidden.php deleted file mode 100644 index 8ced457686..0000000000 --- a/modules/metastore/modules/metastore_search/src/Plugin/search_api/processor/DkanDatasetFilterHidden.php +++ /dev/null @@ -1,28 +0,0 @@ -dataStorage->isHidden($dataset_id); - } - -} diff --git a/modules/metastore/modules/metastore_search/src/Plugin/search_api/processor/DkanDatasetFilterUnpublished.php b/modules/metastore/modules/metastore_search/src/Plugin/search_api/processor/DkanDatasetFilterUnpublished.php deleted file mode 100644 index f49037b660..0000000000 --- a/modules/metastore/modules/metastore_search/src/Plugin/search_api/processor/DkanDatasetFilterUnpublished.php +++ /dev/null @@ -1,28 +0,0 @@ -dataStorage->isPublished($dataset_id); - } - -} diff --git a/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/DkanDatasetFilterProcessorBaseTest.php b/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/DkanDatasetFilterProcessorBaseTest.php deleted file mode 100644 index 2599488cb8..0000000000 --- a/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/DkanDatasetFilterProcessorBaseTest.php +++ /dev/null @@ -1,43 +0,0 @@ -add(DatasourceInterface::class, 'getPluginId', 'dkan_dataset') - ->getMock(); - $index_with_dkan_datasource = (new Chain($this)) - ->add(IndexInterface::class, 'getDatasources', [$dkan_datasource]) - ->getMock(); - $this->assertTrue(DkanDatasetFilterProcessorBase::supportsIndex($index_with_dkan_datasource)); - - // Test filter processor doesn't support indexes without dkan datasources. - $non_dkan_datasource = (new Chain($this)) - ->add(DatasourceInterface::class, 'getPluginId', 'non_dkan_dataset') - ->getMock(); - $index_without_dkan_datasource = (new Chain($this)) - ->add(IndexInterface::class, 'getDatasources', [$non_dkan_datasource]) - ->getMock(); - $this->assertFalse(DkanDatasetFilterProcessorBase::supportsIndex($index_without_dkan_datasource)); - } - -} diff --git a/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/datasource/DkanDatasetTest.php b/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/datasource/DkanDatasetTest.php index ff469d8b90..b5a686cf65 100644 --- a/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/datasource/DkanDatasetTest.php +++ b/modules/metastore/modules/metastore_search/tests/src/Unit/Plugin/search_api/datasource/DkanDatasetTest.php @@ -12,6 +12,7 @@ use Drupal\node\NodeInterface; use MockChain\Chain; use MockChain\Options; +use MockChain\Sequence; use PHPUnit\Framework\TestCase; use Drupal\Core\Entity\EntityTypeRepository; use Drupal\metastore_search\ComplexData\Dataset; @@ -34,22 +35,22 @@ public function test() { ->add('dkan.metastore.storage', DataFactory::class) ->index(0); - $nodeMock = (new Chain($this)) - ->add(NodeInterface::class, 'uuid', 'xyz') - ->getMock(); + $nids = [1, 2]; + $executeSequence = (new Sequence())->add(2)->add($nids); $container = (new Chain($this)) ->add(Container::class, 'get', $containerOptions) ->add(EntityTypeManager::class, 'getStorage', EntityStorageInterface::class) ->add(EntityStorageInterface::class, 'getQuery', QueryInterface::class) - ->add(EntityStorageInterface::class, 'loadMultiple', [$nodeMock, $nodeMock]) + ->add(EntityStorageInterface::class, 'load', NodeInterface::class) + ->add(NodeInterface::class, 'uuid', 'xyz') ->add(QueryInterface::class, 'accessCheck', QueryInterface::class) ->add(QueryInterface::class, 'condition', QueryInterface::class) ->add(QueryInterface::class, 'count', QueryInterface::class) - ->add(QueryInterface::class, 'execute', [1, 2]) + ->add(QueryInterface::class, 'execute', $executeSequence) ->add(QueryInterface::class, 'range', QueryInterface::class) ->add(EntityTypeRepository::class, 'getEntityTypeFromClass', NULL) ->add(DataFactory::class, 'getInstance', Data::class) - ->add(Data::class, 'retrieve', '{}') + ->add(Data::class, 'retrievePublished', '{}') ->getMock(); \Drupal::setContainer($container); diff --git a/modules/metastore/src/Controller/MetastoreController.php b/modules/metastore/src/Controller/MetastoreController.php index d7850088e9..e0f1e93d98 100644 --- a/modules/metastore/src/Controller/MetastoreController.php +++ b/modules/metastore/src/Controller/MetastoreController.php @@ -122,9 +122,6 @@ public function getAll(string $schema_id, Request $request) { * * @return \Symfony\Component\HttpFoundation\JsonResponse * The json response. - * - * @throws \InvalidArgumentException - * When an unpublished or invalid resource is requested. */ public function get(string $schema_id, string $identifier, Request $request) { try { diff --git a/modules/metastore/src/LifeCycle/LifeCycle.php b/modules/metastore/src/LifeCycle/LifeCycle.php index ca4cefc0dd..960d475fbd 100644 --- a/modules/metastore/src/LifeCycle/LifeCycle.php +++ b/modules/metastore/src/LifeCycle/LifeCycle.php @@ -185,12 +185,12 @@ protected function distributionPredelete(MetastoreItemInterface $data) { $resource = $storage->retrieve($distributionUuid); $resource = json_decode($resource); - $id = $resource->data->{'%Ref:downloadURL'}[0]->data->identifier ?? NULL; + $id = $resource->data->{'%Ref:downloadURL'}[0]->data->identifier; + $perspective = $resource->data->{'%Ref:downloadURL'}[0]->data->perspective; + $version = $resource->data->{'%Ref:downloadURL'}[0]->data->version; // Ensure a valid resource ID was found since it's required. if (isset($id)) { - $perspective = $resource->data->{'%Ref:downloadURL'}[0]->data->perspective ?? NULL; - $version = $resource->data->{'%Ref:downloadURL'}[0]->data->version ?? NULL; $this->queueFactory->get('orphan_resource_remover')->createItem([ $id, $perspective, diff --git a/modules/metastore/src/Service.php b/modules/metastore/src/Service.php index a3189b98ad..e55c01debf 100644 --- a/modules/metastore/src/Service.php +++ b/modules/metastore/src/Service.php @@ -106,7 +106,7 @@ public function getSchema($identifier) { * @return \Drupal\metastore\Storage\MetastoreStorageInterface * Entity storage. */ - protected function getStorage(string $schema_id): MetastoreStorageInterface { + private function getStorage(string $schema_id): MetastoreStorageInterface { if (!isset($this->storages[$schema_id])) { $this->storages[$schema_id] = $this->storageFactory->getInstance($schema_id); } @@ -211,21 +211,6 @@ function ($jsonString) use ($schema_id) { }, $jsonStringsArray); } - /** - * Determine whether the given metastore item is published. - * - * @param string $schema_id - * The metastore schema in question. - * @param string $identifier - * The ID of the metastore item in question. - * - * @return bool - * Whether the given metastore item is published. - */ - public function isPublished(string $schema_id, string $identifier): bool { - return $this->getStorage($schema_id)->isPublished($identifier); - } - /** * Implements GET method. * @@ -237,8 +222,8 @@ public function isPublished(string $schema_id, string $identifier): bool { * @return \RootedData\RootedJsonData * The json data. */ - public function get(string $schema_id, string $identifier): RootedJsonData { - $json_string = $this->getStorage($schema_id)->retrieve($identifier, TRUE); + public function get($schema_id, $identifier): RootedJsonData { + $json_string = $this->getStorage($schema_id)->retrievePublished($identifier); $data = $this->validMetadataFactory->get($json_string, $schema_id); $data = $this->dispatchEvent(self::EVENT_DATA_GET, $data); diff --git a/modules/metastore/src/Storage/Data.php b/modules/metastore/src/Storage/Data.php index 3267f0df2e..e63209226c 100644 --- a/modules/metastore/src/Storage/Data.php +++ b/modules/metastore/src/Storage/Data.php @@ -4,7 +4,7 @@ use Drupal\common\LoggerTrait; use Drupal\Core\Entity\ContentEntityInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Entity\RevisionLogInterface; use Drupal\metastore\Exception\MissingObjectException; @@ -21,7 +21,7 @@ abstract class Data implements MetastoreEntityStorageInterface { /** * Entity type manager. * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManager */ protected $entityTypeManager; @@ -84,10 +84,10 @@ abstract class Data implements MetastoreEntityStorageInterface { /** * Constructor. */ - public function __construct(string $schemaId, EntityTypeManagerInterface $entityTypeManager) { + public function __construct(string $schemaId, EntityTypeManager $entityTypeManager) { $this->entityTypeManager = $entityTypeManager; $this->entityStorage = $this->entityTypeManager->getStorage($this->entityType); - $this->schemaId = $schemaId; + $this->setSchema($schemaId); } /** @@ -100,6 +100,13 @@ public function getEntityStorage() { return $this->entityStorage; } + /** + * Private. + */ + private function setSchema($schemaId) { + $this->schemaId = $schemaId; + } + /** * Create basic query for a list of metastore items. * @@ -161,21 +168,14 @@ public function retrieveIds(?int $start = NULL, ?int $length = NULL, bool $unpub * * {@inheritdoc}. */ - public function isHidden(string $uuid): bool { + public function retrievePublished(string $uuid) : ?string { $entity = $this->getEntityPublishedRevision($uuid); - return isset($entity) && ($entity->moderation_state->value ?? NULL) === 'hidden'; - } - - /** - * Inherited. - * - * {@inheritdoc}. - */ - public function isPublished(string $uuid): bool { - $entity = $this->getEntityPublishedRevision($uuid); + if ($entity && $entity->get('moderation_state')->getString() == 'published') { + return $entity->get($this->metadataField)->getString(); + } - return isset($entity) && boolval($entity->status->value ?? FALSE); + throw new MissingObjectException("Error retrieving published dataset: {$this->schemaId} {$uuid} not found."); } /** @@ -183,19 +183,20 @@ public function isPublished(string $uuid): bool { * * {@inheritdoc}. */ - public function retrieve(string $uuid, bool $published = FALSE) : ?string { - if ($published || $this->getDefaultModerationState() === 'published') { + public function retrieve(string $uuid) : ?string { + + if ($this->getDefaultModerationState() === 'published') { $entity = $this->getEntityPublishedRevision($uuid); } else { $entity = $this->getEntityLatestRevision($uuid); } - if (!isset($entity)) { - throw new MissingObjectException("Error retrieving metadata: {$this->schemaId} {$uuid} not found."); + if ($entity) { + return $entity->get($this->metadataField)->getString(); } - return $entity->get($this->metadataField)->getString(); + throw new MissingObjectException("Error retrieving metadata: {$this->schemaId} {$uuid} not found."); } /** diff --git a/modules/metastore/src/Storage/MetastoreStorageInterface.php b/modules/metastore/src/Storage/MetastoreStorageInterface.php index a7ca14ed5b..acfe5eb98a 100644 --- a/modules/metastore/src/Storage/MetastoreStorageInterface.php +++ b/modules/metastore/src/Storage/MetastoreStorageInterface.php @@ -19,20 +19,26 @@ interface MetastoreStorageInterface { public function count(bool $unpublished = FALSE): int; /** - * Retrieve a metadata string by ID. + * Retrieve a metadata string by ID, regardless of whether it is published. * * @param string $id * The identifier for the data. - * @param bool $published - * Whether to retrieve the published revision of the metadata. * * @return string|HydratableInterface * The data or null if no data could be retrieved. + */ + public function retrieve(string $id); + + /** + * Retrieve the json metadata from an entity only if it is published. * - * @throws \Drupal\metastore\Exception\MissingObjectException - * When attempting to retrieve metadata fails. + * @param string $uuid + * The identifier. + * + * @return string|null + * The entity's json metadata, or NULL if the entity was not found. */ - public function retrieve(string $id, bool $published = FALSE); + public function retrievePublished(string $uuid) : ?string; /** * Retrieve all metadata items. @@ -81,17 +87,6 @@ public function retrieveIds(?int $start, ?int $length, bool $unpublished): array */ public function retrieveContains(string $string, bool $caseSensitive): array; - /** - * Determine whether the given metastore item is published. - * - * @param string $uuid - * The ID of the metastore item in question. - * - * @return bool - * Whether the given metastore item is published. - */ - public function isPublished(string $uuid) : bool; - /** * Publish the latest version of a data entity. * diff --git a/modules/metastore/src/Storage/NodeData.php b/modules/metastore/src/Storage/NodeData.php index d223346bc1..ecd67abae6 100644 --- a/modules/metastore/src/Storage/NodeData.php +++ b/modules/metastore/src/Storage/NodeData.php @@ -2,7 +2,7 @@ namespace Drupal\metastore\Storage; -use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\EntityTypeManager; /** * Node Data. @@ -12,7 +12,7 @@ class NodeData extends Data { /** * NodeData constructor. */ - public function __construct(string $schemaId, EntityTypeManagerInterface $entityTypeManager) { + public function __construct(string $schemaId, EntityTypeManager $entityTypeManager) { $this->entityType = 'node'; $this->bundle = 'data'; $this->bundleKey = "type"; diff --git a/modules/metastore/tests/src/Functional/Storage/NodeDataTest.php b/modules/metastore/tests/src/Functional/Storage/NodeDataTest.php index f52617d32b..9852b21d95 100644 --- a/modules/metastore/tests/src/Functional/Storage/NodeDataTest.php +++ b/modules/metastore/tests/src/Functional/Storage/NodeDataTest.php @@ -63,7 +63,7 @@ public function testStorageRetrieveMethods() { $this->assertEquals("456", $allIds[1]); $this->expectException(MissingObjectException::class); - $datasetStorage->retrieve('abc'); + $datasetStorage->retrievePublished('abc'); } diff --git a/modules/metastore/tests/src/Unit/MetastoreControllerTest.php b/modules/metastore/tests/src/Unit/MetastoreControllerTest.php index 0f69b5d4c3..e88c7eb99a 100644 --- a/modules/metastore/tests/src/Unit/MetastoreControllerTest.php +++ b/modules/metastore/tests/src/Unit/MetastoreControllerTest.php @@ -15,11 +15,6 @@ use Drupal\metastore\NodeWrapper\Data as NodeWrapperData; use Drupal\metastore\NodeWrapper\NodeDataFactory; use Drupal\metastore\SchemaRetriever; -use Drupal\metastore\Storage\NodeData; -use Drupal\Core\Entity\EntityStorageInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Entity\Query\QueryInterface; - use MockChain\Chain; use MockChain\Options; use PHPUnit\Framework\TestCase; @@ -92,33 +87,14 @@ public function testGetAllRefs() { * */ public function testGet() { - $schema_id = 'dataset'; - $identifier = 1; - $json = '{"name":"hello"}'; $jsonWithRefs = '{"name": "hello", "%Ref:name": {"identifier": "123", "data": []}}'; - $mockChain = $this->getCommonMockChain() - ->add(Service::class, 'get', new RootedJsonData($jsonWithRefs)); + $mockChain = $this->getCommonMockChain(); + $mockChain->add(Service::class, 'get', new RootedJsonData($jsonWithRefs)); $controller = MetastoreController::create($mockChain->getMock()); - $response = $controller->get($schema_id, $identifier, new Request()); + $response = $controller->get('dataset', 1, new Request()); $this->assertEquals($json, $response->getContent()); - - $entityTypeManagerMock = (new Chain($this)) - ->add(EntityTypeManagerInterface::class, 'getStorage', EntityStorageInterface::class) - ->add(EntityStorageInterface::class, 'getQuery', QueryInterface::class) - ->add(QueryInterface::class, 'accessCheck', QueryInterface::class) - ->add(QueryInterface::class, 'condition', QueryInterface::class) - ->add(QueryInterface::class, 'execute', NULL) - ->getMock(); - $nodeDataMock = new NodeData($schema_id, $entityTypeManagerMock); - $container = $this->getCommonMockChain() - ->add(Service::class, 'getStorage', $nodeDataMock) - ->getMock(); - $controller = MetastoreController::create($container); - $response = $controller->get($schema_id, $identifier, new Request()); - $json = json_decode($response->getContent()); - $this->assertEquals("Error retrieving metadata: {$schema_id} {$identifier} not found.", $json->message); } @@ -512,7 +488,6 @@ private function getCommonMockChain() { ->add(Service::class, 'getSchemas', ['dataset']) ->add(Service::class, 'getSchema', (object) ["id" => "http://schema"]) ->add(Service::class, 'getValidMetadataFactory', ValidMetadataFactory::class) - ->add(Service::class, 'isPublished', TRUE) ->add(MetastoreApiResponse::class, 'getMetastoreItemFactory', NodeDataFactory::class) ->add(MetastoreApiResponse::class, 'addReferenceDependencies', NULL) ->add(NodeDataFactory::class, 'getInstance', NodeWrapperData::class) diff --git a/modules/metastore/tests/src/Unit/ServiceTest.php b/modules/metastore/tests/src/Unit/ServiceTest.php index 4aabf5804f..3fe7ff6a7b 100644 --- a/modules/metastore/tests/src/Unit/ServiceTest.php +++ b/modules/metastore/tests/src/Unit/ServiceTest.php @@ -12,9 +12,7 @@ use Drupal\metastore\Service; use Drupal\metastore\SchemaRetriever; use Drupal\metastore\Storage\DataFactory; -use Drupal\metastore\Storage\MetastoreStorageInterface; use Drupal\metastore\Storage\NodeData; - use MockChain\Chain; use MockChain\Sequence; use PHPUnit\Framework\TestCase; @@ -38,18 +36,6 @@ protected function setUp(): void { $this->validMetadataFactory = self::getValidMetadataFactory($this); } - /** - * Test \Drupal\metastore\Service::isPublished() method. - */ - public function testIsPublished() { - $service = (new Chain($this)) - ->add(Service::class, 'getStorage', MetastoreStorageInterface::class) - ->add(MetastoreStorageInterface::class, 'isPublished', TRUE) - ->getMock(); - - $this->assertTrue($service->isPublished('dataset', 1)); - } - /** * Get a dataset. */ @@ -57,7 +43,7 @@ public function testGet() { $data = $this->validMetadataFactory->get(json_encode(['foo' => 'bar']), 'dataset'); $container = self::getCommonMockChain($this) - ->add(NodeData::class, 'retrieve', json_encode(['foo' => 'bar'])) + ->add(NodeData::class, "retrievePublished", json_encode(['foo' => 'bar'])) ->add(ValidMetadataFactory::class, 'get', $data); \Drupal::setContainer($container->getMock()); diff --git a/tests/src/Functional/DatasetTest.php b/tests/src/Functional/DatasetTest.php index 85ed5082e9..4035f157c2 100644 --- a/tests/src/Functional/DatasetTest.php +++ b/tests/src/Functional/DatasetTest.php @@ -303,13 +303,6 @@ private function getData(string $identifier, string $title, array $downloadUrls) $data->modified = "06-04-2020"; $data->keyword = ["some keyword"]; $data->distribution = []; - $data->publisher = (object) [ - 'name' => 'Test Publisher', - ]; - $data->contactPoint = (object) [ - 'fn' => 'Test Name', - 'hasEmail' => 'test@example.com', - ]; foreach ($downloadUrls as $key => $downloadUrl) { $distribution = new \stdClass();