From d34d524465989925bc84039617b29c5615b1e6fc Mon Sep 17 00:00:00 2001 From: nmykhailets <94851929+nmykhailets@users.noreply.github.com> Date: Mon, 14 Mar 2022 20:54:59 +0200 Subject: [PATCH] chore(samples): interactive tutorials code samples for import user events (#152) * chore: import user events interactive samples --- .../events/import-user-events-big-query.js | 87 ++++++++++ .../events/import-user-events-gcs.js | 94 +++++++++++ .../events/import-user-events-inline.js | 92 +++++++++++ .../product/set-inventory.js | 4 +- .../setup/delete-events-bigquery-table.js | 31 ++++ .../setup/delete-events-gcs-bucket.js | 32 ++++ .../setup/events-create-bigquery-table.js | 45 ++++++ .../setup/events-create-gcs-bucket.js | 52 ++++++ .../setup/update-user-events-json.js | 51 ++++++ .../test/add-fulfillment.test.js | 95 ----------- .../test/import-user-events-big-query.test.js | 78 +++++++++ .../test/import-user-events-gcs.test.js | 78 +++++++++ .../test/import-user-events-inline.test.js | 67 ++++++++ .../test/remove-fulfillment.test.js | 98 ------------ .../search-with-query-expansion-spec.test.js | 1 - .../test/set-inventory.test.js | 151 ------------------ 16 files changed, 709 insertions(+), 347 deletions(-) create mode 100644 retail/interactive-tutorials/events/import-user-events-big-query.js create mode 100644 retail/interactive-tutorials/events/import-user-events-gcs.js create mode 100644 retail/interactive-tutorials/events/import-user-events-inline.js create mode 100644 retail/interactive-tutorials/setup/delete-events-bigquery-table.js create mode 100644 retail/interactive-tutorials/setup/delete-events-gcs-bucket.js create mode 100644 retail/interactive-tutorials/setup/events-create-bigquery-table.js create mode 100644 retail/interactive-tutorials/setup/events-create-gcs-bucket.js create mode 100644 retail/interactive-tutorials/setup/update-user-events-json.js delete mode 100644 retail/interactive-tutorials/test/add-fulfillment.test.js create mode 100644 retail/interactive-tutorials/test/import-user-events-big-query.test.js create mode 100644 retail/interactive-tutorials/test/import-user-events-gcs.test.js create mode 100644 retail/interactive-tutorials/test/import-user-events-inline.test.js delete mode 100644 retail/interactive-tutorials/test/remove-fulfillment.test.js delete mode 100644 retail/interactive-tutorials/test/set-inventory.test.js diff --git a/retail/interactive-tutorials/events/import-user-events-big-query.js b/retail/interactive-tutorials/events/import-user-events-big-query.js new file mode 100644 index 0000000000..2c49490084 --- /dev/null +++ b/retail/interactive-tutorials/events/import-user-events-big-query.js @@ -0,0 +1,87 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(datasetId) { + // [START retail_import_user_events_big_query] + + // Imports the Google Cloud client library. + const {UserEventServiceClient} = require('@google-cloud/retail').v2; + + // Instantiates a client. + const retailClient = new UserEventServiceClient(); + + const projectId = await retailClient.getProjectId(); + const dataSchema = 'user_event'; + const tableId = 'events'; // TO CHECK ERROR HANDLING USE THE TABLE OF INVALID USER EVENTS + + // Placement + const parent = `projects/${projectId}/locations/global/catalogs/default_catalog`; // TO CHECK ERROR HANDLING PASTE THE INVALID CATALOG NAME HERE + + // The desired input location of the data. + const inputConfig = { + bigQuerySource: { + projectId, + datasetId, + tableId, + dataSchema, + }, + }; + + const IResponseParams = { + IImportUserEventsResponse: 0, + IImportMetadata: 1, + IOperation: 2, + }; + + const callImportUserEvents = async () => { + // Construct request + const request = { + parent, + inputConfig, + }; + + console.log('Import request: ', request); + + // Run request + const [operation] = await retailClient.importUserEvents(request); + const response = await operation.promise(); + const result = response[IResponseParams.IImportMetadata]; + console.log( + `Number of successfully imported events: ${result.successCount | 0}` + ); + console.log( + `Number of failures during the importing: ${result.failureCount | 0}` + ); + console.log(`Operation result: ${JSON.stringify(response)}`); + }; + + console.log('Start events import'); + await callImportUserEvents(); + console.log('Events import finished'); + // [END retail_import_user_events_big_query] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main( + ...(() => { + const argv = process.argv.slice(2); + return argv.length ? argv : ['user_events']; + })() +); diff --git a/retail/interactive-tutorials/events/import-user-events-gcs.js b/retail/interactive-tutorials/events/import-user-events-gcs.js new file mode 100644 index 0000000000..e42a908acc --- /dev/null +++ b/retail/interactive-tutorials/events/import-user-events-gcs.js @@ -0,0 +1,94 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(bucketName) { + // [START retail_import_user_events_gcs] + + // Imports the Google Cloud client library. + const {UserEventServiceClient} = require('@google-cloud/retail').v2; + + // Instantiates a client. + const retailClient = new UserEventServiceClient(); + + const projectId = await retailClient.getProjectId(); + + //TODO(developer) set the environment variable value which will be used as the bucket name + const gcsBucket = `gs://${bucketName}`; + const gcsErrorsBucket = `gs://${bucketName}/error`; + const gcsEventsObject = 'user_events.json'; // TO CHECK ERROR HANDLING USE THE JSON WITH INVALID USER EVENTS + + // Placement + const parent = `projects/${projectId}/locations/global/catalogs/default_catalog`; // TO CHECK ERROR HANDLING PASTE THE INVALID CATALOG NAME HERE + + // The desired input location of the data. + const inputConfig = { + gcsSource: { + inputUris: [gcsBucket + '/' + gcsEventsObject], + dataSchema: 'user_event', + }, + }; + + // The desired location of errors incurred during the Import. + const errorsConfig = { + gcsPrefix: gcsErrorsBucket, + }; + + const IResponseParams = { + IImportUserEventsResponse: 0, + IImportMetadata: 1, + IOperation: 2, + }; + + const callImportUserEvents = async () => { + // Construct request + const request = { + parent, + inputConfig, + errorsConfig, + }; + + console.log('Import request: ', request); + + // Run request + const [operation] = await retailClient.importUserEvents(request); + const response = await operation.promise(); + const result = response[IResponseParams.IImportMetadata]; + console.log( + `Number of successfully imported events: ${result.successCount | 0}` + ); + console.log( + `Number of failures during the importing: ${result.failureCount | 0}` + ); + console.log(`Operation result: ${JSON.stringify(response)}`); + }; + + console.log('Start events import'); + await callImportUserEvents(); + console.log('Events import finished'); + // [END retail_import_user_events_gcs] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main( + ...(() => { + const argv = process.argv.slice(2); + return argv.length ? argv : [process.env['EVENTS_BUCKET_NAME']]; + })() +); diff --git a/retail/interactive-tutorials/events/import-user-events-inline.js b/retail/interactive-tutorials/events/import-user-events-inline.js new file mode 100644 index 0000000000..99a628e1a5 --- /dev/null +++ b/retail/interactive-tutorials/events/import-user-events-inline.js @@ -0,0 +1,92 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main() { + // [START retail_import_user_events_inline] + + // Imports the Google Cloud client library. + const {UserEventServiceClient} = require('@google-cloud/retail').v2; + + // Instantiates a client. + const retailClient = new UserEventServiceClient(); + + const projectId = await retailClient.getProjectId(); + + // Placement + const parent = `projects/${projectId}/locations/global/catalogs/default_catalog`; + + // Create events + const generateEvent = eventType => { + return { + eventType, + visitorId: 'visitor_' + Math.random().toString(36).slice(2), + eventTime: { + seconds: Math.round(Date.now() / 1000), + }, + }; + }; + + // The desired input location of the data. + const inputConfig = { + userEventInlineSource: { + userEvents: [ + generateEvent('home-page-view'), + generateEvent('home-page-view'), + generateEvent('home-page-view'), + ], + }, + }; + + const IResponseParams = { + IImportUserEventsResponse: 0, + IImportMetadata: 1, + IOperation: 2, + }; + + const callImportUserEvents = async () => { + // Construct request + const request = { + parent, + inputConfig, + }; + + console.log('Import request: ', request); + + // Run request + const [operation] = await retailClient.importUserEvents(request); + const response = await operation.promise(); + const result = response[IResponseParams.IImportMetadata]; + console.log( + `Number of successfully imported events: ${result.successCount | 0}` + ); + console.log( + `Number of failures during the importing: ${result.failureCount | 0}` + ); + console.log(`Operation result: ${JSON.stringify(response)}`); + }; + + console.log('Start events import'); + await callImportUserEvents(); + console.log('Events import finished'); + // [END retail_import_user_events_inline] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(); diff --git a/retail/interactive-tutorials/product/set-inventory.js b/retail/interactive-tutorials/product/set-inventory.js index 6d8586c592..28f1ca5d13 100644 --- a/retail/interactive-tutorials/product/set-inventory.js +++ b/retail/interactive-tutorials/product/set-inventory.js @@ -81,7 +81,7 @@ async function main(generatedProductId) { // Set inventory with current time console.log('Start set inventory'); await callSetInventory(); - await utils.delay(180000); + await utils.delay(200000); // Get product let changedProduct = await utils.getProduct(createdProduct.name); @@ -94,7 +94,7 @@ async function main(generatedProductId) { product.priceInfo.price = 20.0; setTime = {seconds: Math.round(Date.now() / 1000) - 86400}; await callSetInventory(); - await utils.delay(180000); + await utils.delay(200000); // Get product changedProduct = await utils.getProduct(createdProduct.name); diff --git a/retail/interactive-tutorials/setup/delete-events-bigquery-table.js b/retail/interactive-tutorials/setup/delete-events-bigquery-table.js new file mode 100644 index 0000000000..37f5e11434 --- /dev/null +++ b/retail/interactive-tutorials/setup/delete-events-bigquery-table.js @@ -0,0 +1,31 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main() { + const utils = require('./setup-cleanup'); + + const dataset = 'user_events'; + + //Delete created bigquery dataset + await utils.deleteBqDataset(dataset); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(); diff --git a/retail/interactive-tutorials/setup/delete-events-gcs-bucket.js b/retail/interactive-tutorials/setup/delete-events-gcs-bucket.js new file mode 100644 index 0000000000..2367677c8a --- /dev/null +++ b/retail/interactive-tutorials/setup/delete-events-gcs-bucket.js @@ -0,0 +1,32 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main() { + const utils = require('./setup-cleanup'); + + // The ID of your GCS bucket + const bucketName = process.env['EVENTS_BUCKET_NAME']; + + //Delete created bucket + await utils.deleteBucket(bucketName); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(); diff --git a/retail/interactive-tutorials/setup/events-create-bigquery-table.js b/retail/interactive-tutorials/setup/events-create-bigquery-table.js new file mode 100644 index 0000000000..5ae2892026 --- /dev/null +++ b/retail/interactive-tutorials/setup/events-create-bigquery-table.js @@ -0,0 +1,45 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main() { + const utils = require('./setup-cleanup'); + + const dataset = 'user_events'; + const validTable = 'events'; + const invalidTable = 'events_some_invalid'; + const schema = 'resources/events_schema.json'; + const validSourceFile = 'resources/user_events.json'; + const invalidSourceFile = 'resources/user_events_some_invalid.json'; + + await utils.createBqDataset(dataset); + await utils.createBqTable(dataset, validTable, schema); + await utils.uploadDataToBqTable(dataset, validTable, validSourceFile, schema); + + await utils.createBqTable(dataset, invalidTable, schema); + await utils.uploadDataToBqTable( + dataset, + invalidTable, + invalidSourceFile, + schema + ); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(); diff --git a/retail/interactive-tutorials/setup/events-create-gcs-bucket.js b/retail/interactive-tutorials/setup/events-create-gcs-bucket.js new file mode 100644 index 0000000000..04f614c07e --- /dev/null +++ b/retail/interactive-tutorials/setup/events-create-gcs-bucket.js @@ -0,0 +1,52 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(generatedBucketName) { + // Imports the Google Cloud client library. + const {UserEventServiceClient} = require('@google-cloud/retail').v2; + const utils = require('./setup-cleanup'); + + // Instantiates a client. + const retailClient = new UserEventServiceClient(); + const projectId = await retailClient.getProjectId(); + + // The ID of your GCS bucket + const bucketName = generatedBucketName + ? generatedBucketName + : `${projectId}_events_${Math.round(Date.now() / 1000)}`; + + //Creates the new bucket + await utils.createBucket(bucketName); + + //Upload files + await utils.uploadFile( + bucketName, + 'resources/user_events.json', + 'user_events.json' + ); + await utils.uploadFile( + bucketName, + 'resources/user_events_some_invalid.json', + 'user_events_some_invalid.json' + ); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/retail/interactive-tutorials/setup/update-user-events-json.js b/retail/interactive-tutorials/setup/update-user-events-json.js new file mode 100644 index 0000000000..b5e5936624 --- /dev/null +++ b/retail/interactive-tutorials/setup/update-user-events-json.js @@ -0,0 +1,51 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const fs = require('fs'); + +async function main() { + const updateEventsTimestamp = filePath => { + const events = fs.readFileSync(filePath).toString().split('\n'); + const changedEvents = []; + + for (let i = 0; i < events.length - 1; ++i) { + const event = JSON.parse(`[${events[i]}]`)[0]; + const date = new Date(event.eventTime); + date.setDate(date.getDate() - 1); + event.eventTime = date.toISOString(); + changedEvents.push(JSON.stringify(event)); + } + + const stream = fs.createWriteStream(filePath); + stream.on('error', error => { + throw error; + }); + changedEvents.forEach(item => { + stream.write(item + '\n'); + }); + stream.close(); + }; + + updateEventsTimestamp('resources/user_events.json'); + updateEventsTimestamp('resources/user_events_some_invalid.json'); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/retail/interactive-tutorials/test/add-fulfillment.test.js b/retail/interactive-tutorials/test/add-fulfillment.test.js deleted file mode 100644 index 080c922d8f..0000000000 --- a/retail/interactive-tutorials/test/add-fulfillment.test.js +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2022 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -'use strict'; - -const path = require('path'); -const cp = require('child_process'); -const {before, describe, it, after} = require('mocha'); -const {ProductServiceClient} = require('@google-cloud/retail'); -const {assert, expect} = require('chai'); - -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); - -const cwd = path.join(__dirname, '..'); - -describe('Add fulfillment', () => { - const retailClient = new ProductServiceClient(); - const productId = Math.random().toString(36).slice(2).toUpperCase(); - const projectNumber = process.env['GCLOUD_PROJECT']; - const name = `projects/${projectNumber}/locations/global/catalogs/default_catalog/branches/default_branch/products/${productId}`; - - let stdout; - - before(async () => { - stdout = execSync( - `node interactive-tutorials/product/add-fulfillment-places.js ${productId}`, - { - cwd, - } - ); - }); - - it('should check that product created', () => { - const regex = new RegExp(`Product ${productId} created`, 'g'); - assert.match(stdout, regex); - }); - - it('should check that add fulfillment started', () => { - assert.match(stdout, /Start add fulfillment/); - }); - - it('should check that add fulfillment finished', () => { - assert.match(stdout, /Add fulfillment finished/); - }); - - it('should check that product updated correctly', async () => { - const regex = new RegExp('Updated product with current time: .*\\n', 'g'); - assert.match(stdout, regex); - const string = stdout - .match(regex) - .toString() - .replace('Updated product with current time: ', ''); - const updatedProduct = JSON.parse(string); - - expect(updatedProduct).to.be.an('object'); - expect(updatedProduct.fulfillmentInfo).to.be.an('array'); - expect( - updatedProduct.fulfillmentInfo.length, - 'Fulfillment array is empty' - ).to.equal(1); - - const item = updatedProduct.fulfillmentInfo[0]; - expect(item).to.be.an('object'); - expect(item).to.have.all.keys('type', 'placeIds'); - expect(item.type).to.equal('same-day-delivery'); - expect(item.placeIds) - .to.be.an('array') - .that.includes('store1', 'store2', 'store3'); - }); - - it('should check that product deleted', async () => { - const regex = new RegExp(`Product ${productId} deleted`, 'g'); - assert.match(stdout, regex); - }); - - after(async () => { - try { - const product = await retailClient.getProduct({name: name}); - expect(product, 'The product not deleted').to.be.undefined; - } catch (err) { - expect(err, 'Bad error code').to.include({code: 5}); - } - }); -}); diff --git a/retail/interactive-tutorials/test/import-user-events-big-query.test.js b/retail/interactive-tutorials/test/import-user-events-big-query.test.js new file mode 100644 index 0000000000..8242d9fc82 --- /dev/null +++ b/retail/interactive-tutorials/test/import-user-events-big-query.test.js @@ -0,0 +1,78 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const cp = require('child_process'); +const {before, after, describe, it} = require('mocha'); +const {assert, expect} = require('chai'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cwd = path.join(__dirname, '..'); +const utils = require('../setup/setup-cleanup'); + +describe('Import user events from big query table', () => { + let stdout; + const dataset = `user_events_${Math.round(Date.now() / 1000)}`; + const table = 'events'; + const schema = 'interactive-tutorials/resources/events_schema.json'; + const sourceFile = 'interactive-tutorials/resources/user_events.json'; + + before(async () => { + await utils.createBqDataset(dataset); + await utils.createBqTable(dataset, table, schema); + await utils.uploadDataToBqTable(dataset, table, sourceFile, schema); + stdout = execSync( + `node interactive-tutorials/events/import-user-events-big-query.js ${dataset}`, + { + cwd, + } + ); + }); + + it('should check that import started', () => { + assert.match(stdout, /Start events import/); + }); + + it('should check that events imported correctly', async () => { + const regex = new RegExp('Operation result: .*\\n', 'g'); + assert.match(stdout, regex); + const string = stdout + .match(regex) + .toString() + .replace('Operation result: ', ''); + const importOperation = JSON.parse(string); + + expect(importOperation).to.be.an('array'); + expect(importOperation.length).to.equal(3); + + const response = importOperation[1]; + const metadata = importOperation[2]; + + expect(metadata).to.be.an('object'); + expect(metadata.done).to.be.true; + + expect(response).to.be.an('object'); + }); + + it('should check that import finished', () => { + assert.match(stdout, /Events import finished/); + }); + + after(async () => { + await utils.deleteBqDataset(dataset); + }); +}); diff --git a/retail/interactive-tutorials/test/import-user-events-gcs.test.js b/retail/interactive-tutorials/test/import-user-events-gcs.test.js new file mode 100644 index 0000000000..826ba5d512 --- /dev/null +++ b/retail/interactive-tutorials/test/import-user-events-gcs.test.js @@ -0,0 +1,78 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const cp = require('child_process'); +const {before, after, describe, it} = require('mocha'); +const {assert, expect} = require('chai'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cwd = path.join(__dirname, '..'); +const utils = require('../setup/setup-cleanup'); + +describe('Import user events from gcs', () => { + let stdout; + const bucketName = `user_events_${Math.round(Date.now() / 1000)}`; + + before(async () => { + await utils.createBucket(bucketName); + await utils.uploadFile( + bucketName, + 'interactive-tutorials/resources/user_events.json', + 'user_events.json' + ); + stdout = execSync( + `node interactive-tutorials/events/import-user-events-gcs.js ${bucketName}`, + { + cwd, + } + ); + }); + + it('should check that import started', () => { + assert.match(stdout, /Start events import/); + }); + + it('should check that events imported correctly', async () => { + const regex = new RegExp('Operation result: .*\\n', 'g'); + assert.match(stdout, regex); + const string = stdout + .match(regex) + .toString() + .replace('Operation result: ', ''); + const importOperation = JSON.parse(string); + + expect(importOperation).to.be.an('array'); + expect(importOperation.length).to.equal(3); + + const response = importOperation[1]; + const metadata = importOperation[2]; + + expect(metadata).to.be.an('object'); + expect(metadata.done).to.be.true; + + expect(response).to.be.an('object'); + }); + + it('should check that import finished', () => { + assert.match(stdout, /Events import finished/); + }); + + after(async () => { + await utils.deleteBucket(bucketName); + }); +}); diff --git a/retail/interactive-tutorials/test/import-user-events-inline.test.js b/retail/interactive-tutorials/test/import-user-events-inline.test.js new file mode 100644 index 0000000000..e2e86aa825 --- /dev/null +++ b/retail/interactive-tutorials/test/import-user-events-inline.test.js @@ -0,0 +1,67 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const cp = require('child_process'); +const {before, describe, it} = require('mocha'); +const {assert, expect} = require('chai'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cwd = path.join(__dirname, '..'); + +describe('Import user events from inline', () => { + let stdout; + + before(async () => { + stdout = execSync( + 'node interactive-tutorials/events/import-user-events-inline.js', + { + cwd, + } + ); + }); + + it('should check that import started', () => { + assert.match(stdout, /Start events import/); + }); + + it('should check that events imported correctly', async () => { + const regex = new RegExp('Operation result: .*\\n', 'g'); + assert.match(stdout, regex); + const string = stdout + .match(regex) + .toString() + .replace('Operation result: ', ''); + const importOperation = JSON.parse(string); + + expect(importOperation).to.be.an('array'); + expect(importOperation.length).to.equal(3); + + const response = importOperation[1]; + const metadata = importOperation[2]; + + expect(metadata).to.be.an('object'); + expect(metadata.done).to.be.true; + + expect(response).to.be.an('object'); + expect(response.successCount).to.equal('3'); + }); + + it('should check that import finished', () => { + assert.match(stdout, /Events import finished/); + }); +}); diff --git a/retail/interactive-tutorials/test/remove-fulfillment.test.js b/retail/interactive-tutorials/test/remove-fulfillment.test.js deleted file mode 100644 index b3d7773344..0000000000 --- a/retail/interactive-tutorials/test/remove-fulfillment.test.js +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2022 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -'use strict'; - -const path = require('path'); -const cp = require('child_process'); -const {before, describe, it, after} = require('mocha'); -const {ProductServiceClient} = require('@google-cloud/retail'); -const {assert, expect} = require('chai'); - -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); - -const cwd = path.join(__dirname, '..'); - -describe('Remove fulfillment', () => { - const retailClient = new ProductServiceClient(); - const productId = Math.random().toString(36).slice(2).toUpperCase(); - const projectNumber = process.env['GCLOUD_PROJECT']; - const name = `projects/${projectNumber}/locations/global/catalogs/default_catalog/branches/default_branch/products/${productId}`; - - let stdout; - - before(async () => { - stdout = execSync( - `node interactive-tutorials/product/remove-fulfillment-places.js ${productId}`, - {cwd} - ); - }); - - it('should check that product created', () => { - const regex = new RegExp(`Product ${productId} created`, 'g'); - assert.match(stdout, regex); - }); - - it('should check that remove fulfillment started', () => { - assert.match(stdout, /Start remove fulfillment/); - }); - - it('should check that add fulfillment finished', () => { - assert.match(stdout, /Remove fulfillment finished/); - }); - - const checkUpdatedProduct = updatedProduct => { - expect(updatedProduct).to.be.an('object'); - expect(updatedProduct.fulfillmentInfo).to.be.an('array'); - expect( - updatedProduct.fulfillmentInfo.length, - 'Fulfillment array is empty' - ).to.equal(1); - - const item = updatedProduct.fulfillmentInfo[0]; - expect(item).to.be.an('object'); - expect(item).to.have.all.keys('type', 'placeIds'); - expect(item.type).to.equal('same-day-delivery'); - expect(item.placeIds) - .to.be.an('array') - .that.includes('store3', 'store2') - .but.not.include('store1'); - }; - - it('should check that fulfillment removed correctly', async () => { - const regex = new RegExp('Updated product with current time: .*\\n', 'g'); - assert.match(stdout, regex); - const string = stdout - .match(regex) - .toString() - .replace('Updated product with current time: ', ''); - const updatedProduct = JSON.parse(string); - - checkUpdatedProduct(updatedProduct); - }); - - it('should check that product deleted', async () => { - const regex = new RegExp(`Product ${productId} deleted`, 'g'); - assert.match(stdout, regex); - }); - - after(async () => { - try { - const product = await retailClient.getProduct({name: name}); - expect(product, 'The product not deleted').to.be.undefined; - } catch (err) { - expect(err, 'Bad error code').to.include({code: 5}); - } - }); -}); diff --git a/retail/interactive-tutorials/test/search-with-query-expansion-spec.test.js b/retail/interactive-tutorials/test/search-with-query-expansion-spec.test.js index 246cc1df65..414523501d 100644 --- a/retail/interactive-tutorials/test/search-with-query-expansion-spec.test.js +++ b/retail/interactive-tutorials/test/search-with-query-expansion-spec.test.js @@ -96,7 +96,6 @@ describe('Search with query expansion spec', () => { it('should contain expanded query', () => { const searchResponse = response[IResponseParams.ISearchResponse]; - console.log(response); expect( searchResponse.queryExpansionInfo, 'Search response does not contain query expansion info' diff --git a/retail/interactive-tutorials/test/set-inventory.test.js b/retail/interactive-tutorials/test/set-inventory.test.js deleted file mode 100644 index 180051166b..0000000000 --- a/retail/interactive-tutorials/test/set-inventory.test.js +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2022 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -'use strict'; - -const path = require('path'); -const cp = require('child_process'); -const {before, describe, it, after} = require('mocha'); -const {ProductServiceClient} = require('@google-cloud/retail'); -const {assert, expect} = require('chai'); - -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); - -const cwd = path.join(__dirname, '..'); - -describe('Set inventory', () => { - const retailClient = new ProductServiceClient(); - const productId = Math.random().toString(36).slice(2).toUpperCase(); - const projectNumber = process.env['GCLOUD_PROJECT']; - const name = `projects/${projectNumber}/locations/global/catalogs/default_catalog/branches/default_branch/products/${productId}`; - const product = { - priceInfo: { - price: 15.0, - originalPrice: 20.0, - cost: 8.0, - currencyCode: 'USD', - }, - fulfillmentInfo: [ - { - type: 'same-day-delivery', - placeIds: ['store3', 'store4'], - }, - ], - availableQuantity: { - value: 2, - }, - availability: 'IN_STOCK', - }; - let stdout; - - before(async () => { - stdout = execSync( - `node interactive-tutorials/product/set-inventory.js ${productId}`, - {cwd} - ); - }); - - it('should check that product created', () => { - const regex = new RegExp(`Product ${productId} created`, 'g'); - assert.match(stdout, regex); - }); - - it('should check that set inventory started', () => { - assert.match(stdout, /Start set inventory/); - }); - - it('should check that set inventory finished', () => { - assert.match(stdout, /Set inventory finished/); - }); - - const checkUpdatedProduct = updatedProduct => { - expect(updatedProduct).to.be.an('object'); - assert.containsAllDeepKeys(updatedProduct, product); - expect(updatedProduct.priceInfo.price, 'Price not equal').to.equal(15.0); - expect( - updatedProduct.priceInfo.originalPrice, - 'Original price not equal' - ).to.equal(20.0); - expect(updatedProduct.priceInfo.cost, 'Cost not equal').to.equal(8.0); - expect( - updatedProduct.priceInfo.currencyCode, - 'Currency code not equal' - ).to.equal('USD'); - expect(updatedProduct.fulfillmentInfo).to.be.an('array'); - expect( - updatedProduct.fulfillmentInfo.length, - 'Fulfillment array is empty' - ).to.equal(1); - - const fulfillmentItem = updatedProduct.fulfillmentInfo[0]; - expect(fulfillmentItem).to.be.an('object'); - expect(fulfillmentItem).to.have.all.keys('type', 'placeIds'); - expect(fulfillmentItem.type).to.equal('same-day-delivery'); - expect(fulfillmentItem.placeIds) - .to.be.an('array') - .that.includes('store3', 'store4'); - - expect( - updatedProduct.availableQuantity, - 'Available quantity not equal' - ).to.deep.equal({value: 2}); - expect(updatedProduct.availability, 'Availability not equal').to.equal( - 'IN_STOCK' - ); - }; - - it('should check that product updated correctly', async () => { - const regex = new RegExp( - `Updated product ${productId} with current time: .*\\n`, - 'g' - ); - assert.match(stdout, regex); - const string = stdout - .match(regex) - .toString() - .replace(`Updated product ${productId} with current time: `, ''); - const updatedProduct = JSON.parse(string); - - checkUpdatedProduct(updatedProduct); - }); - - it('should check that product has not been updated with outdated time', async () => { - const regex = new RegExp( - `Updated product ${productId} with outdated time: .*\\n`, - 'g' - ); - assert.match(stdout, regex); - const string = stdout - .match(regex) - .toString() - .replace(`Updated product ${productId} with outdated time: `, ''); - const updatedProduct = JSON.parse(string); - - checkUpdatedProduct(updatedProduct); - }); - - it('should check that product deleted', async () => { - const regex = new RegExp(`Product ${productId} deleted`, 'g'); - assert.match(stdout, regex); - }); - - after(async () => { - try { - const product = await retailClient.getProduct({name: name}); - expect(product, 'The product not deleted').to.be.undefined; - } catch (err) { - expect(err, 'Bad error code').to.include({code: 5}); - } - }); -});