From 978b95ed20903c829e074d82eb8c1e3dc8810181 Mon Sep 17 00:00:00 2001 From: Elad Iwanir <13205761+eladiw@users.noreply.github.com> Date: Wed, 6 Nov 2019 18:23:16 -0800 Subject: [PATCH] Indexers - createOrUpdate, List indexers and matching UTs (#222) * indexers create - wip * Adds APIs for checking synonym map existence (#209) * Adds APIs for checking synonym map existence * Adds createOrUpdateSynonymMap client methods (#210) * createOrUpdateSynonym map if not exists tests (#216) createOrUpdateSynonymMapIfNotExistsFailsOnExistingResource and createOrUpdateSynonymMapIfNotExistsSucceedsOnNoResource tests * createOrUpdateSynonym map if exists tests (#217) createOrUpdateSynonymMapIfExistsSucceedsOnExistingResource and createOrUpdateSynonymMapIfExistsSucceedsOnExistingResource tests * assertReflectionEquals - index management objects (#214) * removing index management equal comparison annd replacing with reflection equal method * Indexers - createOrUpdate, List indexers and matching UTs * style fix * fix merge issues * style fixes * changed list overload * changed list overload * changed list overload * fix style --- .../search/SearchServiceAsyncClient.java | 288 +++++++++----- .../com/azure/search/SearchServiceClient.java | 200 ++++++---- .../com/azure/search/DataSourceTestBase.java | 1 + .../azure/search/IndexManagementTestBase.java | 1 - .../search/IndexersManagementAsyncTests.java | 84 ++++ .../search/IndexersManagementSyncTests.java | 85 ++++ .../search/IndexersManagementTestBase.java | 90 +++++ .../azure/search/SearchServiceTestBase.java | 365 +++++++++++------- .../canCreateAndListIndexers.json | 125 ++++++ ...exerFailsWithUsefulMessageOnUserError.json | 28 ++ ...createIndexerReturnsCorrectDefinition.json | 29 ++ 11 files changed, 973 insertions(+), 323 deletions(-) create mode 100644 sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementAsyncTests.java create mode 100644 sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementSyncTests.java create mode 100644 sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementTestBase.java create mode 100644 sdk/search/azure-search/src/test/resources/session-records/canCreateAndListIndexers.json create mode 100644 sdk/search/azure-search/src/test/resources/session-records/createIndexerFailsWithUsefulMessageOnUserError.json create mode 100644 sdk/search/azure-search/src/test/resources/session-records/createIndexerReturnsCorrectDefinition.json diff --git a/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceAsyncClient.java b/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceAsyncClient.java index 5e709e7e52ba9..1af4d32917684 100644 --- a/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceAsyncClient.java +++ b/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceAsyncClient.java @@ -25,7 +25,6 @@ import com.azure.search.models.IndexGetStatisticsResult; import com.azure.search.models.Indexer; import com.azure.search.models.IndexerExecutionInfo; -import com.azure.search.models.IndexerListResult; import com.azure.search.models.RequestOptions; import com.azure.search.models.Skillset; import com.azure.search.models.SkillsetListResult; @@ -207,7 +206,7 @@ public Mono getDataSource(String dataSourceName) { * * @param dataSourceName the name of the data source to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @return the DataSource. */ public Mono getDataSource(String dataSourceName, RequestOptions requestOptions) { @@ -220,7 +219,7 @@ public Mono getDataSource(String dataSourceName, RequestOptions requ * * @param dataSourceName the name of the data source to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @param context Context * @return a response containing the DataSource. */ @@ -246,9 +245,8 @@ public PagedFlux listDataSources() { * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. - * + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @return a list of DataSources */ public PagedFlux listDataSources(String select) { @@ -259,10 +257,10 @@ public PagedFlux listDataSources(String select) { * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @param requestOptions Additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @return a list of DataSources */ public PagedFlux listDataSources(String select, RequestOptions requestOptions) { @@ -281,10 +279,10 @@ PagedFlux listDataSources(String select, RequestOptions requestOptio * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @param requestOptions Additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @return a response containing the list of DataSources. */ public Mono> listDataSourcesWithResponse(String select, RequestOptions requestOptions) { @@ -305,24 +303,6 @@ Mono> listDataSourcesWithResponse(String select, ); } - /** - * @return the updated DataSource. - * @throws NotImplementedException not implemented - */ - public Mono createOrUpdateDataSource() { - throw logger.logExceptionAsError( - new NotImplementedException("not implemented.")); - } - - /** - * @return a response containing the updated DataSource. - * @throws NotImplementedException not implemented - */ - public Mono> createOrUpdateDataSourceWithResponse() { - throw logger.logExceptionAsError( - new NotImplementedException("not implemented.")); - } - /** * Delete a DataSource * @@ -344,14 +324,14 @@ public Mono> deleteDataSource(String dataSourceName) { * @return a valid Mono */ public Mono> deleteDataSource(String dataSourceName, - RequestOptions requestOptions, - AccessCondition accessCondition) { + RequestOptions requestOptions, + AccessCondition accessCondition) { return withContext(context -> - deleteDataSourceWithResponse( - dataSourceName, - requestOptions, - accessCondition, - context)); + deleteDataSourceWithResponse( + dataSourceName, + requestOptions, + accessCondition, + context)); } /** @@ -363,14 +343,14 @@ public Mono> deleteDataSource(String dataSourceName, * @return a mono response */ public Mono> deleteDataSourceWithResponse(String dataSourceName, - RequestOptions requestOptions, - AccessCondition accessCondition) { + RequestOptions requestOptions, + AccessCondition accessCondition) { return withContext(context -> deleteDataSourceWithResponse( - dataSourceName, - requestOptions, - accessCondition, - context)); + dataSourceName, + requestOptions, + accessCondition, + context)); } /** @@ -382,33 +362,89 @@ public Mono> deleteDataSourceWithResponse(String dataSourceName, * @return a mono response */ Mono> deleteDataSourceWithResponse(String dataSourceName, - RequestOptions requestOptions, - AccessCondition accessCondition, - Context context) { + RequestOptions requestOptions, + AccessCondition accessCondition, + Context context) { return restClient.dataSources() - .deleteWithRestResponseAsync( - dataSourceName, - requestOptions, - accessCondition, - context).map(Function.identity()); + .deleteWithRestResponseAsync( + dataSourceName, + requestOptions, + accessCondition, + context).map(Function.identity()); } /** - * @return the created Indexer. - * @throws NotImplementedException not implemented + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @return a response containing the created Indexer. */ - public Mono createIndexer() { - throw logger.logExceptionAsError( - new NotImplementedException("not implemented.")); + public Mono createOrUpdateIndexer(Indexer indexer) { + return createOrUpdateIndexer(indexer, null, null); } /** + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @param requestOptions additional parameters for the operation. + * Contains the tracking ID sent with the request to help with debugging + * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or + * doesn't match specified values * @return a response containing the created Indexer. - * @throws NotImplementedException not implemented */ - public Mono> createIndexerWithResponse() { - throw logger.logExceptionAsError( - new NotImplementedException("not implemented.")); + public Mono createOrUpdateIndexer(Indexer indexer, + RequestOptions requestOptions, + AccessCondition accessCondition) { + return createOrUpdateIndexerWithResponse( + indexer, + requestOptions, + accessCondition) + .map(Response::getValue); + } + + /** + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @param requestOptions additional parameters for the operation. + * Contains the tracking ID sent with the request to help with debugging + * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or + * doesn't match specified values + * @return a response containing the created Indexer. + */ + public Mono> createOrUpdateIndexerWithResponse(Indexer indexer, + RequestOptions requestOptions, + AccessCondition accessCondition) { + return withContext(context -> createOrUpdateIndexerWithResponse( + indexer, + requestOptions, + accessCondition, + context)); + } + + /** + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @param requestOptions additional parameters for the operation. + * Contains the tracking ID sent with the request to help with debugging + * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or + * doesn't match specified values + * @param context Context + * @return A response object containing the Indexer. + */ + Mono> createOrUpdateIndexerWithResponse(Indexer indexer, + RequestOptions requestOptions, + AccessCondition accessCondition, + Context context) { + return restClient.indexers().createOrUpdateWithRestResponseAsync( + indexer.getName(), + indexer, + requestOptions, + accessCondition, + context) + .map(Function.identity()); } /** @@ -431,34 +467,81 @@ public Mono> getIndexerWithResponse() { /** * @return all Indexers from the Search service. - * @throws NotImplementedException not implemented */ - public Mono listIndexers() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public PagedFlux listIndexers() { + return new PagedFlux<>( + () -> listIndexersWithResponse(null, null), + nextLink -> Mono.empty()); } /** + * Lists all indexers available for an Azure Search service. + * + * @param select Selects which top-level properties of the indexers to retrieve. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. + * @param requestOptions Additional parameters for the operation. * @return a response containing all Indexers from the Search service. - * @throws NotImplementedException not implemented */ - public Mono> listIndexersWithResponse() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public PagedFlux listIndexers(String select, RequestOptions requestOptions) { + return new PagedFlux<>( + () -> listIndexersWithResponse(select, requestOptions), + nextLink -> Mono.empty()); } /** - * @return the updated Indexer. - * @throws NotImplementedException not implemented + * Lists all indexers available for an Azure Search service. + * + * @param select Selects which top-level properties of the indexers to retrieve. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. + * @param requestOptions Additional parameters for the operation. + * @param context The context to associate with this operation. + * @return a response containing all Indexers from the Search service. */ - public Mono createOrUpdateIndexer() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + PagedFlux listIndexers(String select, RequestOptions requestOptions, Context context) { + return new PagedFlux<>( + () -> listIndexersWithResponse(select, requestOptions, context), + nextLink -> Mono.empty()); } /** - * @return a response containing the updated Indexer. - * @throws NotImplementedException not implemented + * Lists all indexers available for an Azure Search service. + * + * @param select Selects which top-level properties of the indexers to retrieve. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. + * @param requestOptions Additional parameters for the operation. + * @return a response containing all Indexers from the Search service. */ - public Mono> createOrUpdateIndexerWithResponse() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public Mono> listIndexersWithResponse(String select, RequestOptions requestOptions) { + return withContext(context -> listIndexersWithResponse(select, requestOptions, context)); + } + + /** + * Lists all indexers available for an Azure Search service. + * + * @param select Selects which top-level properties of the indexers to retrieve. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. + * @param requestOptions Additional parameters for the operation. + * @param context The context to associate with this operation. + * @return a response containing all Indexers from the Search service. + */ + Mono> listIndexersWithResponse(String select, + RequestOptions requestOptions, + Context context) { + return restClient + .indexers() + .listWithRestResponseAsync(select, requestOptions, context) + .map(response -> new PagedResponseBase<>( + response.getRequest(), + response.getStatusCode(), + response.getHeaders(), + response.getValue().getIndexers(), + null, + deserializeHeaders(response.getHeaders())) + ); } /** @@ -682,8 +765,8 @@ public PagedFlux listIndexes() { * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @return a reactive response emitting the list of indexes. */ public PagedFlux listIndexes(String select) { @@ -694,8 +777,8 @@ public PagedFlux listIndexes(String select) { * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @return a reactive response emitting the list of indexes. @@ -716,8 +799,8 @@ PagedFlux listIndexes(String select, RequestOptions requestOptions, Conte * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @return a response emitting the list of indexes. @@ -763,7 +846,7 @@ public Mono createOrUpdateIndex(Index index) { */ public Mono createOrUpdateIndex(Index index, boolean allowIndexDowntime) { return this.createOrUpdateIndexWithResponse(index, allowIndexDowntime, null, null) - .map(Response::getValue); + .map(Response::getValue); } /** @@ -795,7 +878,7 @@ public Mono createOrUpdateIndex(Index index, */ public Mono createOrUpdateIndex(Index index, boolean allowIndexDowntime, AccessCondition accessCondition) { return this.createOrUpdateIndexWithResponse(index, allowIndexDowntime, accessCondition, null) - .map(Response::getValue); + .map(Response::getValue); } /** @@ -886,9 +969,9 @@ public Mono deleteIndex(String indexName) { */ public Mono deleteIndex(String indexName, AccessCondition accessCondition) { return this.deleteIndexWithResponse(indexName, - accessCondition, - null) - .flatMap(FluxUtil::toMono); + accessCondition, + null) + .flatMap(FluxUtil::toMono); } /** @@ -957,7 +1040,7 @@ public PagedFlux analyzeIndex(String indexName, AnalyzeRequest analyz * @param indexName the name of the index for which to test an analyzer * @param analyzeRequest the text and analyzer or analysis components to test * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return a response containing analyze result. */ public PagedFlux analyzeIndex(String indexName, @@ -981,9 +1064,9 @@ PagedFlux analyzeIndex(String indexName, } Mono> analyzeIndexWithResponse(String indexName, - AnalyzeRequest analyzeRequest, - RequestOptions requestOptions, - Context context) { + AnalyzeRequest analyzeRequest, + RequestOptions requestOptions, + Context context) { return restClient.indexes() .analyzeWithRestResponseAsync(indexName, analyzeRequest, requestOptions, context) .map(response -> new PagedResponseBase<>( @@ -1121,7 +1204,8 @@ Mono> createSynonymMapWithResponse(SynonymMap synonymMap, .map(Function.identity()); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @return the {@link SynonymMap} definition @@ -1131,11 +1215,12 @@ public Mono getSynonymMap(String synonymMapName) { .map(Response::getValue); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return the {@link SynonymMap} definition */ public Mono getSynonymMap(String synonymMapName, RequestOptions requestOptions) { @@ -1143,11 +1228,12 @@ public Mono getSynonymMap(String synonymMapName, RequestOptions requ .map(Response::getValue); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return a response containing the SynonymMap. */ public Mono> getSynonymMapWithResponse(String synonymMapName, @@ -1326,7 +1412,7 @@ public Mono deleteSynonymMap(String synonymMapName) { * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @return a response signalling completion. */ public Mono deleteSynonymMap(String synonymMapName, AccessCondition accessCondition) { @@ -1338,9 +1424,9 @@ public Mono deleteSynonymMap(String synonymMapName, AccessCondition access * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return a response signalling completion. */ public Mono deleteSynonymMap(String synonymMapName, @@ -1355,9 +1441,9 @@ public Mono deleteSynonymMap(String synonymMapName, * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return a response signalling completion. */ public Mono> deleteSynonymMapWithResponse(String synonymMapName, @@ -1392,7 +1478,7 @@ public Mono synonymMapExists(String synonymMapName) { * * @param synonymMapName the name of the synonym map * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return true if the synonym map exists; false otherwise. */ public Mono synonymMapExists(String synonymMapName, RequestOptions requestOptions) { @@ -1404,7 +1490,7 @@ public Mono synonymMapExists(String synonymMapName, RequestOptions requ * * @param synonymMapName the name of the synonym map * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return true if the synonym map exists; false otherwise. */ public Mono> synonymMapExistsWithResponse(String synonymMapName, RequestOptions requestOptions) { diff --git a/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceClient.java b/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceClient.java index 5e3ca0a43f5a6..7eccdd206ef86 100644 --- a/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceClient.java +++ b/sdk/search/azure-search/src/main/java/com/azure/search/SearchServiceClient.java @@ -16,7 +16,6 @@ import com.azure.search.models.IndexGetStatisticsResult; import com.azure.search.models.Indexer; import com.azure.search.models.IndexerExecutionInfo; -import com.azure.search.models.IndexerListResult; import com.azure.search.models.RequestOptions; import com.azure.search.models.Skillset; import com.azure.search.models.SkillsetListResult; @@ -144,7 +143,7 @@ public DataSource getDataSource(String dataSourceName) { * * @param dataSourceName the name of the data source to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @return the DataSource. */ public DataSource getDataSource(String dataSourceName, RequestOptions requestOptions) { @@ -156,13 +155,13 @@ public DataSource getDataSource(String dataSourceName, RequestOptions requestOpt * * @param dataSourceName the name of the data source to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @param context Context * @return a response containing the DataSource. */ public Response getDataSourceWithResponse(String dataSourceName, - RequestOptions requestOptions, - Context context) { + RequestOptions requestOptions, + Context context) { return asyncClient.getDataSourceWithResponse(dataSourceName, requestOptions, context).block(); } @@ -179,9 +178,8 @@ public PagedIterable listDataSources() { * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. - * + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @return a list of DataSources */ public PagedIterable listDataSources(String select) { @@ -192,11 +190,10 @@ public PagedIterable listDataSources(String select) { * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @param requestOptions Additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. - * + * Contains the tracking ID sent with the request to help with debugging. * @return a list of DataSources */ public PagedIterable listDataSources(String select, RequestOptions requestOptions) { @@ -207,12 +204,11 @@ public PagedIterable listDataSources(String select, RequestOptions r * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @param requestOptions Additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @param context Additional context that is passed through the HTTP pipeline during the service call. - * * @return a list of DataSources */ public PagedIterable listDataSources(String select, RequestOptions requestOptions, Context context) { @@ -223,12 +219,11 @@ public PagedIterable listDataSources(String select, RequestOptions r * List all DataSources from an Azure Cognitive Search service. * * @param select Selects which top-level properties of DataSource definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. * @param requestOptions Additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging. + * Contains the tracking ID sent with the request to help with debugging. * @param context Additional context that is passed through the HTTP pipeline during the service call. - * * @return a response containing the list of DataSources. */ public PagedResponse listDataSourcesWithResponse(String select, @@ -304,50 +299,107 @@ public Response createIndexerWithResponse() { } /** - * @return the Indexer. - * @throws NotImplementedException not implemented + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @return a response containing the created Indexer. */ - public Indexer getIndexer() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public Indexer createOrUpdateIndexer(Indexer indexer) { + return asyncClient.createOrUpdateIndexer(indexer).block(); } /** - * @return a response containing the Indexer. - * @throws NotImplementedException not implemented + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @param requestOptions additional parameters for the operation. + * Contains the tracking ID sent with the request to help with debugging + * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or + * doesn't match specified values + * @return a response containing the created Indexer. */ - public Response getIndexerWithResponse() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public Indexer createOrUpdateIndexer(Indexer indexer, + RequestOptions requestOptions, + AccessCondition accessCondition) { + return asyncClient.createOrUpdateIndexer( + indexer, + requestOptions, + accessCondition).block(); + } + + /** + * Creates a new Azure Search indexer or updates an indexer if it already exists. + * + * @param indexer The definition of the indexer to create or update. + * @param requestOptions additional parameters for the operation. + * Contains the tracking ID sent with the request to help with debugging + * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or + * doesn't match specified values + * @param context Context + * @return A response object containing the Indexer. + */ + public Response createOrUpdateIndexerWithResponse(Indexer indexer, + RequestOptions requestOptions, + AccessCondition accessCondition, + Context context) { + return asyncClient.createOrUpdateIndexerWithResponse( + indexer, + requestOptions, + accessCondition, + context) + .block(); } /** * @return all Indexers from the Search service. - * @throws NotImplementedException not implemented */ - public IndexerListResult listIndexers() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public PagedIterable listIndexers() { + return new PagedIterable<>(asyncClient.listIndexers()); + } + + /** + * Lists all indexers available for an Azure Search service. + * + * @param select Selects which top-level properties of the indexers to retrieve. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. + * @param requestOptions Additional parameters for the operation. + * @param context The context to associate with this operation. + * @return all Indexers from the Search service. + */ + public PagedIterable listIndexers(String select, RequestOptions requestOptions, Context context) { + return new PagedIterable<>(asyncClient.listIndexers(select, requestOptions, context)); } /** + * Lists all indexers available for an Azure Search service. + * + * @param select Selects which top-level properties of the indexers to retrieve. + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties. + * @param requestOptions Additional parameters for the operation. + * @param context The context to associate with this operation. * @return a response containing all Indexers from the Search service. - * @throws NotImplementedException not implemented */ - public Response listIndexersWithResponse() { - throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); + public PagedResponse listIndexersWithResponse(String select, + RequestOptions requestOptions, + Context context) { + return asyncClient.listIndexersWithResponse(select, requestOptions, context).block(); } /** - * @return the updated Indexer. + * @return the Indexer. * @throws NotImplementedException not implemented */ - public Indexer createOrUpdateIndexer() { + public Indexer getIndexer() { throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); } /** - * @return a response containing the updated Indexer. + * @return a response containing the Indexer. * @throws NotImplementedException not implemented */ - public Response createOrUpdateIndexerWithResponse() { + public Response getIndexerWithResponse() { throw logger.logExceptionAsError(new NotImplementedException("not implemented.")); } @@ -591,8 +643,8 @@ public PagedIterable listIndexes() { * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @return the list of indexes. */ public PagedIterable listIndexes(String select) { @@ -603,8 +655,8 @@ public PagedIterable listIndexes(String select) { * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @return the list of indexes. @@ -617,8 +669,8 @@ public PagedIterable listIndexes(String select, RequestOptions requestOpt * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the HTTP pipeline during the service call @@ -632,8 +684,8 @@ public PagedIterable listIndexes(String select, RequestOptions requestOpt * Lists all indexes available for an Azure Cognitive Search service. * * @param select selects which top-level properties of the index definitions to retrieve. - * Specified as a comma-separated list of JSON property names, or '*' for all properties. - * The default is all properties + * Specified as a comma-separated list of JSON property names, or '*' for all properties. + * The default is all properties * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @param context Additional context that is passed through the HTTP pipeline during the service call. @@ -707,7 +759,7 @@ public Index createOrUpdateIndex(Index index, boolean allowIndexDowntime, Access * can be impaired for several minutes after the index is updated, or longer for very * large indexes. * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @return the index that was created or updated. @@ -759,7 +811,7 @@ public Index createOrUpdateIndex(Index index, * can be impaired for several minutes after the index is updated, or longer for very * large indexes. * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the HTTP pipeline during the service call @@ -804,7 +856,7 @@ public void deleteIndex(String indexName, AccessCondition accessCondition) { * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging */ public void deleteIndex(String indexName, AccessCondition accessCondition, RequestOptions requestOptions) { asyncClient.deleteIndex(indexName, accessCondition, requestOptions).block(); @@ -835,7 +887,7 @@ public void deleteIndex(String indexName, * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the Http pipeline during the service call * @return a response signalling completion. */ @@ -867,7 +919,7 @@ public PagedIterable analyzeIndex(String indexName, * @param indexName the name of the index for which to test an analyzer * @param analyzeRequest the text and analyzer or analysis components to test * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return analyze result. */ public PagedIterable analyzeIndex(String indexName, @@ -882,7 +934,7 @@ public PagedIterable analyzeIndex(String indexName, * @param indexName the name of the index for which to test an analyzer * @param analyzeRequest the text and analyzer or analysis components to test * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the HTTP pipeline during the service call * @return a response containing analyze result. */ @@ -1024,7 +1076,8 @@ public Response createSynonymMapWithResponse(SynonymMap synonymMap, context).block(); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @return the {@link SynonymMap} definition @@ -1033,22 +1086,24 @@ public SynonymMap getSynonymMap(String synonymMapName) { return asyncClient.getSynonymMap(synonymMapName).block(); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return the {@link SynonymMap} definition. */ public SynonymMap getSynonymMap(String synonymMapName, RequestOptions requestOptions) { return asyncClient.getSynonymMap(synonymMapName, requestOptions).block(); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context a context that is passed through the HTTP pipeline during the service call * @return the {@link SynonymMap} definition. */ @@ -1058,11 +1113,12 @@ public SynonymMap getSynonymMap(String synonymMapName, return this.getSynonymMapWithResponse(synonymMapName, requestOptions, context).getValue(); } - /** Retrieves a synonym map definition. + /** + * Retrieves a synonym map definition. * * @param synonymMapName name of the synonym map to retrieve * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context a context that is passed through the HTTP pipeline during the service call * @return a response containing the SynonymMap. */ @@ -1234,7 +1290,7 @@ public void deleteSynonymMap(String synonymMapName) { * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values */ public void deleteSynonymMap(String synonymMapName, AccessCondition accessCondition) { asyncClient.deleteSynonymMap(synonymMapName, accessCondition).block(); @@ -1245,9 +1301,9 @@ public void deleteSynonymMap(String synonymMapName, AccessCondition accessCondit * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging */ public void deleteSynonymMap(String synonymMapName, AccessCondition accessCondition, @@ -1260,16 +1316,16 @@ public void deleteSynonymMap(String synonymMapName, * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the Http pipeline during the service call * Contains the tracking ID sent with the request to help with debugging */ public void deleteSynonymMap(String synonymMapName, - AccessCondition accessCondition, - RequestOptions requestOptions, - Context context) { + AccessCondition accessCondition, + RequestOptions requestOptions, + Context context) { this.deleteSynonymMapWithResponse(synonymMapName, accessCondition, requestOptions, @@ -1281,9 +1337,9 @@ public void deleteSynonymMap(String synonymMapName, * * @param synonymMapName the name of the synonym map to delete * @param accessCondition the condition where the operation will be performed if the ETag on the server matches or - * doesn't match specified values + * doesn't match specified values * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the Http pipeline during the service call * @return a response signalling completion. */ @@ -1312,7 +1368,7 @@ public Boolean synonymMapExists(String synonymMapName) { * * @param synonymMapName the name of the synonym map * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @return true if the synonym map exists; false otherwise. */ public Boolean synonymMapExists(String synonymMapName, RequestOptions requestOptions) { @@ -1324,7 +1380,7 @@ public Boolean synonymMapExists(String synonymMapName, RequestOptions requestOpt * * @param synonymMapName the name of the synonym map * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the HTTP pipeline during the service call * @return true if the index exists; false otherwise. */ @@ -1338,7 +1394,7 @@ public Boolean synonymMapExists(String synonymMapName, RequestOptions requestOpt * * @param synonymMapName the name of the synonym map * @param requestOptions additional parameters for the operation. - * Contains the tracking ID sent with the request to help with debugging + * Contains the tracking ID sent with the request to help with debugging * @param context additional context that is passed through the HTTP pipeline during the service call * @return true if the index exists; false otherwise. */ diff --git a/sdk/search/azure-search/src/test/java/com/azure/search/DataSourceTestBase.java b/sdk/search/azure-search/src/test/java/com/azure/search/DataSourceTestBase.java index e28a99235bdf3..475a30806ca6b 100644 --- a/sdk/search/azure-search/src/test/java/com/azure/search/DataSourceTestBase.java +++ b/sdk/search/azure-search/src/test/java/com/azure/search/DataSourceTestBase.java @@ -17,6 +17,7 @@ import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; public abstract class DataSourceTestBase extends SearchServiceTestBase { + private static final String FAKE_DESCRIPTION = "Some data source"; private static final String RESOURCE_NAME_PREFIX = "azs-"; // The connection string we use here, as well as table name and target index schema, use the USGS database diff --git a/sdk/search/azure-search/src/test/java/com/azure/search/IndexManagementTestBase.java b/sdk/search/azure-search/src/test/java/com/azure/search/IndexManagementTestBase.java index 34cc3a3e3d5b3..d50a75c2f748c 100644 --- a/sdk/search/azure-search/src/test/java/com/azure/search/IndexManagementTestBase.java +++ b/sdk/search/azure-search/src/test/java/com/azure/search/IndexManagementTestBase.java @@ -5,7 +5,6 @@ import com.azure.search.models.Field; import com.azure.search.models.Index; import org.junit.Test; - import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; import static org.unitils.reflectionassert.ReflectionComparatorMode.IGNORE_DEFAULTS; diff --git a/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementAsyncTests.java b/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementAsyncTests.java new file mode 100644 index 0000000000000..19de02baa9c8f --- /dev/null +++ b/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementAsyncTests.java @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.search; + +import com.azure.core.exception.HttpResponseException; +import com.azure.core.http.rest.PagedFlux; +import com.azure.search.models.DataSource; +import com.azure.search.models.Index; +import com.azure.search.models.Indexer; +import com.azure.search.models.IndexingParameters; +import org.junit.Assert; +import reactor.test.StepVerifier; + +import java.util.HashMap; +import java.util.Map; + +public class IndexersManagementAsyncTests extends IndexersManagementTestBase { + private SearchServiceAsyncClient client; + + @Override + public void createIndexerReturnsCorrectDefinition() { + client = getSearchServiceClientBuilder().buildAsyncClient(); + Indexer expectedIndexer = createTestIndexer("indexer"); + expectedIndexer.setIsDisabled(true); + expectedIndexer.setParameters( + new IndexingParameters() + .setBatchSize(50) + .setMaxFailedItems(10) + .setMaxFailedItemsPerBatch(10)); + + Indexer actualIndexer = client.createOrUpdateIndexer(expectedIndexer).block(); + + IndexingParameters ip = new IndexingParameters(); + Map config = new HashMap<>(); + ip.setConfiguration(config); + expectedIndexer.setParameters(ip); // Get returns empty dictionary. + expectSameStartTime(expectedIndexer, actualIndexer); + + assertIndexersEqual(expectedIndexer, actualIndexer); + } + + @Override + public void canCreateAndListIndexers() { + client = getSearchServiceClientBuilder().buildAsyncClient(); + + // Create the data source, note it a valid DS with actual + // connection string + DataSource datasource = createTestSqlDataSource(); + client.createOrUpdateDataSource(datasource).block(); + + // Create an index + Index index = createTestIndexForLiveDatasource(); + client.createOrUpdateIndex(index).block(); + + + // Create two indexers + Indexer indexer1 = createTestIndexer("i1"); + Indexer indexer2 = createTestIndexer("i2"); + client.createOrUpdateIndexer(indexer1).block(); + client.createOrUpdateIndexer(indexer2).block(); + + PagedFlux returnedIndexersLst = client.listIndexers(); + StepVerifier + .create(returnedIndexersLst.collectList()) + .assertNext(indexers -> { + Assert.assertEquals(2, indexers.size()); + Assert.assertTrue(indexers.get(0).getName().equals(indexer1.getName())); + Assert.assertTrue(indexers.get(1).getName().equals(indexer2.getName())); + }) + .verifyComplete(); + } + + @Override + public void createIndexerFailsWithUsefulMessageOnUserError() { + client = getSearchServiceClientBuilder().buildAsyncClient(); + Indexer indexer = createTestIndexer("indexer"); + indexer.setDataSourceName("thisdatasourcedoesnotexist"); + + assertException( + () -> client.createOrUpdateIndexer(indexer).block(), + HttpResponseException.class, + "This indexer refers to a data source 'thisdatasourcedoesnotexist' that doesn't exist"); + } +} diff --git a/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementSyncTests.java b/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementSyncTests.java new file mode 100644 index 0000000000000..1a2ed6d06d1b8 --- /dev/null +++ b/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementSyncTests.java @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.search; + +import com.azure.core.exception.HttpResponseException; +import com.azure.search.models.DataSource; +import com.azure.search.models.Index; +import com.azure.search.models.Indexer; +import com.azure.search.models.IndexingParameters; +import org.junit.Assert; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class IndexersManagementSyncTests extends IndexersManagementTestBase { + private SearchServiceClient client; + + @Override + public void createIndexerReturnsCorrectDefinition() { + client = getSearchServiceClientBuilder().buildClient(); + Indexer expectedIndexer = + createTestIndexer("indexer") + .setIsDisabled(true) + .setParameters( + new IndexingParameters() + .setBatchSize(50) + .setMaxFailedItems(10) + .setMaxFailedItemsPerBatch(10)); + + Indexer actualIndexer = client.createOrUpdateIndexer(expectedIndexer); + + IndexingParameters ip = new IndexingParameters(); + Map config = new HashMap<>(); + ip.setConfiguration(config); + expectedIndexer.setParameters(ip); // Get returns empty dictionary. + expectSameStartTime(expectedIndexer, actualIndexer); + + assertIndexersEqual(expectedIndexer, actualIndexer); + } + + @Override + public void canCreateAndListIndexers() { + client = getSearchServiceClientBuilder().buildClient(); + + // Create the data source, note it a valid DS with actual + // connection string + DataSource datasource = createTestSqlDataSource(); + client.createOrUpdateDataSource(datasource); + + // Create an index + Index index = createTestIndexForLiveDatasource(); + client.createOrUpdateIndex(index); + + + // Create two indexers + Indexer indexer1 = createTestIndexer("i1"); + Indexer indexer2 = createTestIndexer("i2"); + client.createOrUpdateIndexer(indexer1); + client.createOrUpdateIndexer(indexer2); + + List indexers = client.listIndexers().stream().collect(Collectors.toList()); + Assert.assertEquals(2, indexers.size()); + + Assert.assertTrue( + indexers.stream() + .anyMatch(item -> indexer1.getName().equals(item.getName()))); + Assert.assertTrue( + indexers.stream() + .anyMatch(item -> indexer2.getName().equals(item.getName()))); + } + + @Override + public void createIndexerFailsWithUsefulMessageOnUserError() { + client = getSearchServiceClientBuilder().buildClient(); + Indexer indexer = createTestIndexer("indexer"); + indexer.setDataSourceName("thisdatasourcedoesnotexist"); + + assertException( + () -> client.createOrUpdateIndexer(indexer), + HttpResponseException.class, + "This indexer refers to a data source 'thisdatasourcedoesnotexist' that doesn't exist"); + } +} diff --git a/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementTestBase.java b/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementTestBase.java new file mode 100644 index 0000000000000..9b6617725aa57 --- /dev/null +++ b/sdk/search/azure-search/src/test/java/com/azure/search/IndexersManagementTestBase.java @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.search; + +import com.azure.search.models.DataType; +import com.azure.search.models.Field; +import com.azure.search.models.Index; +import com.azure.search.models.Indexer; +import com.azure.search.models.IndexingSchedule; +import org.junit.Assert; +import org.junit.Test; + +import java.time.Duration; +import java.util.Arrays; + +import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; +import static org.unitils.reflectionassert.ReflectionComparatorMode.IGNORE_DEFAULTS; + +public abstract class IndexersManagementTestBase extends SearchServiceTestBase { + + @Test + public abstract void createIndexerReturnsCorrectDefinition(); + + @Test + public abstract void canCreateAndListIndexers(); + + @Test + public abstract void createIndexerFailsWithUsefulMessageOnUserError(); + + protected void assertIndexersEqual(Indexer expected, Indexer actual) { + expected.setETag("none"); + actual.setETag("none"); + + // we ignore defaults as when properties are not set they are returned from the service with + // default values + assertReflectionEquals(expected, actual, IGNORE_DEFAULTS); + } + + protected Indexer createTestIndexer(String indexerName) { + return new Indexer() + .setName(indexerName) + .setDataSourceName("azs-java-test-sql") + .setTargetIndexName("indexforindexers") + .setSchedule(new IndexingSchedule().setInterval(Duration.ofDays(1))); + } + + protected static void expectSameStartTime(Indexer expected, Indexer actual) { + // There ought to be a start time in the response; We just can't know what it is because it would + // make the test timing-dependent. + expected.getSchedule().setStartTime(actual.getSchedule().getStartTime()); + } + + /** + * This index contains fields that are declared on the live datasource + * we use to test the indexers + * + * @return the newly created Index object + */ + protected Index createTestIndexForLiveDatasource() { + return new Index() + .setName("indexforindexers") + .setFields(Arrays.asList( + new Field() + .setName("county_name") + .setType(DataType.EDM_STRING) + .setSearchable(Boolean.FALSE) + .setFilterable(Boolean.TRUE), + new Field() + .setName("state") + .setType(DataType.EDM_STRING) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE), + new Field() + .setName("feature_id") + .setType(DataType.EDM_STRING) + .setKey(Boolean.TRUE) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.FALSE))); + } + + void assertException(Runnable exceptionThrower, Class expectedExceptionType, String expectedMessage) { + try { + exceptionThrower.run(); + Assert.fail(); + } catch (Throwable ex) { + Assert.assertEquals(expectedExceptionType, ex.getClass()); + Assert.assertTrue(ex.getMessage().contains(expectedMessage)); + } + } +} diff --git a/sdk/search/azure-search/src/test/java/com/azure/search/SearchServiceTestBase.java b/sdk/search/azure-search/src/test/java/com/azure/search/SearchServiceTestBase.java index 7a9ea823fae54..5b729bf7a322f 100644 --- a/sdk/search/azure-search/src/test/java/com/azure/search/SearchServiceTestBase.java +++ b/sdk/search/azure-search/src/test/java/com/azure/search/SearchServiceTestBase.java @@ -12,9 +12,13 @@ import com.azure.core.implementation.serializer.jsonwrapper.jacksonwrapper.JacksonDeserializer; import com.azure.core.test.TestBase; import com.azure.core.util.Configuration; +import com.azure.search.common.DataSources; import com.azure.search.models.AccessCondition; import com.azure.search.models.AnalyzerName; import com.azure.search.models.CorsOptions; +import com.azure.search.models.DataChangeDetectionPolicy; +import com.azure.search.models.DataDeletionDetectionPolicy; +import com.azure.search.models.DataSource; import com.azure.search.models.DataType; import com.azure.search.models.DistanceScoringFunction; import com.azure.search.models.DistanceScoringParameters; @@ -53,6 +57,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import static com.azure.search.DataSourceTestBase.azureSql; import java.util.Objects; public abstract class SearchServiceTestBase extends TestBase { @@ -60,6 +65,18 @@ public abstract class SearchServiceTestBase extends TestBase { private static final String DEFAULT_DNS_SUFFIX = "search.windows.net"; private static final String DOGFOOD_DNS_SUFFIX = "search-dogfood.windows-int.net"; + private static final String FAKE_DESCRIPTION = "Some data source"; + private static final String RESOURCE_NAME_PREFIX = "azs-"; + // The connection string we use here, as well as table name and target index schema, use the USGS database + // that we set up to support our code samples. + // + // ASSUMPTION: Change tracking has already been enabled on the database with ALTER DATABASE ... SET CHANGE_TRACKING = ON + // and it has been enabled on the table with ALTER TABLE ... ENABLE CHANGE_TRACKING + private static final String SQL_CONN_STRING_FIXTURE = "Server=tcp:xxx.database.windows.net,1433;Database=xxx;User ID=reader;Password=xxx;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;"; + + public static final String AZURE_SQL_CONN_STRING_READONLY = + "Server=tcp:azs-playground.database.windows.net,1433;Database=usgs;User ID=reader;Password=EdrERBt3j6mZDP;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;"; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline")] + private String searchServiceName; private String searchDnsSuffix; protected String endpoint; @@ -127,42 +144,6 @@ protected SearchServiceClientBuilder getSearchServiceClientBuilder() { } } - private static void initializeAzureResources() { - String appId = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_CLIENT_ID); - String azureDomainId = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_TENANT_ID); - String secret = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_CLIENT_SECRET); - String subscriptionId = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_SUBSCRIPTION_ID); - - testEnvironment = Configuration.getGlobalConfiguration().get("AZURE_TEST_ENVIRONMENT"); - if (testEnvironment == null) { - testEnvironment = "AZURE"; - } else { - testEnvironment = testEnvironment.toUpperCase(Locale.US); - } - - AzureEnvironment environment = testEnvironment.equals("DOGFOOD") ? getDogfoodEnvironment() : AzureEnvironment.AZURE; - - ApplicationTokenCredentials applicationTokenCredentials = new ApplicationTokenCredentials( - appId, - azureDomainId, - secret, - environment); - - azureSearchResources = new AzureSearchResources(applicationTokenCredentials, subscriptionId, Region.US_EAST); - } - - private static AzureEnvironment getDogfoodEnvironment() { - HashMap configuration = new HashMap<>(); - configuration.put("portalUrl", "http://df.onecloud.azure-test.net"); - configuration.put("managementEndpointUrl", "https://management.core.windows.net/"); - configuration.put("resourceManagerEndpointUrl", "https://api-dogfood.resources.windows-int.net/"); - configuration.put("activeDirectoryEndpointUrl", "https://login.windows-ppe.net/"); - configuration.put("activeDirectoryResourceId", "https://management.core.windows.net/"); - configuration.put("activeDirectoryGraphResourceId", "https://graph.ppe.windows.net/"); - configuration.put("activeDirectoryGraphApiVersion", "2013-04-05"); - return new AzureEnvironment(configuration); - } - protected Index createTestIndex() { Map weights = new HashMap(); weights.put("Description", 1.5); @@ -173,93 +154,93 @@ protected Index createTestIndex() { new Field() .setName("HotelId") .setType(DataType.EDM_STRING) - .setKey(true) - .setSearchable(false) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setKey(Boolean.TRUE) + .setSearchable(Boolean.FALSE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("HotelName") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(true) - .setFacetable(false) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.FALSE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Description") .setType(DataType.EDM_STRING) - .setKey(false) - .setSearchable(true) - .setFilterable(false) - .setSortable(false) - .setFacetable(false) + .setKey(Boolean.FALSE) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.FALSE) + .setSortable(Boolean.FALSE) + .setFacetable(Boolean.FALSE) .setAnalyzer(AnalyzerName.EN_LUCENE) - .setRetrievable(true), + .setRetrievable(Boolean.TRUE), new Field() .setName("DescriptionFr") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(false) - .setSortable(false) - .setFacetable(false) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.FALSE) + .setSortable(Boolean.FALSE) + .setFacetable(Boolean.FALSE) .setAnalyzer(AnalyzerName.FR_LUCENE) - .setRetrievable(true), + .setRetrievable(Boolean.TRUE), new Field() .setName("Description_Custom") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(false) - .setSortable(false) - .setFacetable(false) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.FALSE) + .setSortable(Boolean.FALSE) + .setFacetable(Boolean.FALSE) .setSearchAnalyzer(AnalyzerName.STOP) .setIndexAnalyzer(AnalyzerName.STOP) - .setRetrievable(true), + .setRetrievable(Boolean.TRUE), new Field() .setName("Category") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Tags") .setType(DataType.COLLECTION_EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(false) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.FALSE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("ParkingIncluded") .setType(DataType.EDM_BOOLEAN) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("SmokingAllowed") .setType(DataType.EDM_BOOLEAN) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("LastRenovationDate") .setType(DataType.EDM_DATE_TIME_OFFSET) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Rating") .setType(DataType.EDM_INT32) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Address") .setType(DataType.EDM_COMPLEX_TYPE) @@ -267,50 +248,50 @@ protected Index createTestIndex() { new Field() .setName("StreetAddress") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("City") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("StateProvince") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Country") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("PostalCode") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(true) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE) ) ), new Field() .setName("Location") .setType(DataType.EDM_GEOGRAPHY_POINT) - .setFilterable(true) - .setSortable(true) - .setFacetable(false) - .setRetrievable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.FALSE) + .setRetrievable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Rooms") .setType(DataType.COLLECTION_EDM_COMPLEX_TYPE) @@ -318,66 +299,66 @@ protected Index createTestIndex() { new Field() .setName("Description") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setRetrievable(true) + .setSearchable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE) .setAnalyzer(AnalyzerName.EN_LUCENE), new Field() .setName("DescriptionFr") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setRetrievable(true) + .setSearchable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE) .setAnalyzer(AnalyzerName.FR_LUCENE), new Field() .setName("Type") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("BaseRate") .setType(DataType.EDM_DOUBLE) - .setKey(false) - .setSearchable(false) - .setFilterable(true) - .setFacetable(true) - .setRetrievable(true), + .setKey(Boolean.FALSE) + .setSearchable(Boolean.FALSE) + .setFilterable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("BedOptions") .setType(DataType.EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setFacetable(true) - .setRetrievable(true), + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("SleepsCount") .setType(DataType.EDM_INT32) - .setFilterable(true) - .setFacetable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("SmokingAllowed") .setType(DataType.EDM_BOOLEAN) - .setFilterable(true) - .setFacetable(true) - .setRetrievable(true), + .setFilterable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE), new Field() .setName("Tags") .setType(DataType.COLLECTION_EDM_STRING) - .setSearchable(true) - .setFilterable(true) - .setSortable(false) - .setFacetable(true) - .setRetrievable(true) + .setSearchable(Boolean.TRUE) + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.FALSE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.TRUE) ) ), new Field() .setName("TotalGuests") .setType(DataType.EDM_INT64) - .setFilterable(true) - .setSortable(true) - .setFacetable(true) - .setRetrievable(false + .setFilterable(Boolean.TRUE) + .setSortable(Boolean.TRUE) + .setFacetable(Boolean.TRUE) + .setRetrievable(Boolean.FALSE ), new Field() .setName("ProfitMargin") @@ -459,6 +440,91 @@ protected Index createTestIndex() { .setSourceFields(Arrays.asList("HotelName")))); } + protected DataSource createTestBlobDataSource(DataDeletionDetectionPolicy deletionDetectionPolicy) { + return DataSources.azureBlobStorage( + "azs-java-test-blob", + "DefaultEndpointsProtocol=https;AccountName=NotaRealAccount;AccountKey=fake;", + "fakecontainer", + "/fakefolder/", + deletionDetectionPolicy, + FAKE_DESCRIPTION + ); + } + + protected DataSource createTestSqlDataSource(DataDeletionDetectionPolicy deletionDetectionPolicy, DataChangeDetectionPolicy changeDetectionPolicy) { + return azureSql( + "azs-java-test-sql", + SQL_CONN_STRING_FIXTURE, + "GeoNamesRI", + changeDetectionPolicy, + deletionDetectionPolicy, + FAKE_DESCRIPTION + ); + } + + protected DataSource createTestSqlDataSource() { + DataDeletionDetectionPolicy deletionDetectionPolicy = null; + return azureSql( + "azs-java-test-sql", + AZURE_SQL_CONN_STRING_READONLY, + "GeoNamesRI", + null, + deletionDetectionPolicy, + FAKE_DESCRIPTION + ); + } + + protected DataSource createTestCosmosDbDataSource( + DataDeletionDetectionPolicy deletionDetectionPolicy, + boolean useChangeDetection) { + + return DataSources.cosmosDb( + "azs-java-test-cosmos", + "AccountEndpoint=https://NotaRealAccount.documents.azure.com;AccountKey=fake;Database=someFakeDatabase", + "faketable", + "SELECT ... FROM x where x._ts > @HighWaterMark", + useChangeDetection, + deletionDetectionPolicy, + FAKE_DESCRIPTION + ); + } + + private static void initializeAzureResources() { + String appId = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_CLIENT_ID); + String azureDomainId = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_TENANT_ID); + String secret = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_CLIENT_SECRET); + String subscriptionId = Configuration.getGlobalConfiguration().get(Configuration.PROPERTY_AZURE_SUBSCRIPTION_ID); + + testEnvironment = Configuration.getGlobalConfiguration().get("AZURE_TEST_ENVIRONMENT"); + if (testEnvironment == null) { + testEnvironment = "AZURE"; + } else { + testEnvironment = testEnvironment.toUpperCase(Locale.US); + } + + AzureEnvironment environment = testEnvironment.equals("DOGFOOD") ? getDogfoodEnvironment() : AzureEnvironment.AZURE; + + ApplicationTokenCredentials applicationTokenCredentials = new ApplicationTokenCredentials( + appId, + azureDomainId, + secret, + environment); + + azureSearchResources = new AzureSearchResources(applicationTokenCredentials, subscriptionId, Region.US_EAST); + } + + private static AzureEnvironment getDogfoodEnvironment() { + HashMap configuration = new HashMap<>(); + configuration.put("portalUrl", "http://df.onecloud.azure-test.net"); + configuration.put("managementEndpointUrl", "https://management.core.windows.net/"); + configuration.put("resourceManagerEndpointUrl", "https://api-dogfood.resources.windows-int.net/"); + configuration.put("activeDirectoryEndpointUrl", "https://login.windows-ppe.net/"); + configuration.put("activeDirectoryResourceId", "https://management.core.windows.net/"); + configuration.put("activeDirectoryGraphResourceId", "https://graph.ppe.windows.net/"); + configuration.put("activeDirectoryGraphApiVersion", "2013-04-05"); + return new AzureEnvironment(configuration); + } + protected SearchIndexClientBuilder getSearchIndexClientBuilder(String indexName) { if (!interceptorManager.isPlaybackMode()) { return new SearchIndexClientBuilder() @@ -622,11 +688,12 @@ protected void waitForIndexing() { /** * If the document schema is known, user can convert the properties to a specific object type + * * @param cls Class type of the document object to convert to * @param type * @return an object of the request type */ - protected T convertToType(Object document, Class cls) { + protected T convertToType(Object document, Class cls) { return jsonApi.convertObjectToType(document, cls); } diff --git a/sdk/search/azure-search/src/test/resources/session-records/canCreateAndListIndexers.json b/sdk/search/azure-search/src/test/resources/session-records/canCreateAndListIndexers.json new file mode 100644 index 0000000000000..32ff50a073762 --- /dev/null +++ b/sdk/search/azure-search/src/test/resources/session-records/canCreateAndListIndexers.json @@ -0,0 +1,125 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/datasources('azs-java-test-sql')?api-version=2019-05-06", + "Headers" : { + "Content-Type" : "application/json; charset=utf-8" + }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "e207b05e-d36d-4ef3-bb2f-1aa653902a1e", + "StatusCode" : "201", + "Date" : "Wed, 06 Nov 2019 23:23:33 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "ETag" : "W/\"0x8D7631057491CE4\"", + "elapsed-time" : "57", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "395", + "Body" : "{\"@odata.context\":\"https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/$metadata#datasources/$entity\",\"@odata.etag\":\"\\\"0x8D7631057491CE4\\\"\",\"name\":\"azs-java-test-sql\",\"description\":\"Some data source\",\"type\":\"azuresql\",\"subtype\":null,\"credentials\":{\"connectionString\":null},\"container\":{\"name\":\"GeoNamesRI\",\"query\":null},\"dataChangeDetectionPolicy\":null,\"dataDeletionDetectionPolicy\":null}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Type" : "application/json; odata.metadata=minimal", + "Location" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/datasources('azs-java-test-sql')?api-version=2019-05-06" + }, + "Exception" : null + }, { + "Method" : "PUT", + "Uri" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexes('indexforindexers')?allowIndexDowntime=false&api-version=2019-05-06", + "Headers" : { + "Content-Type" : "application/json; charset=utf-8" + }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "dc6b1a60-7402-438e-b0ba-f6e854b4bc77", + "StatusCode" : "201", + "Date" : "Wed, 06 Nov 2019 23:23:33 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "ETag" : "W/\"0x8D7631057C7687E\"", + "elapsed-time" : "719", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "982", + "Body" : "{\"@odata.context\":\"https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/$metadata#indexes/$entity\",\"@odata.etag\":\"\\\"0x8D7631057C7687E\\\"\",\"name\":\"indexforindexers\",\"defaultScoringProfile\":null,\"fields\":[{\"name\":\"county_name\",\"type\":\"Edm.String\",\"searchable\":false,\"filterable\":true,\"retrievable\":true,\"sortable\":true,\"facetable\":true,\"key\":false,\"indexAnalyzer\":null,\"searchAnalyzer\":null,\"analyzer\":null,\"synonymMaps\":[]},{\"name\":\"state\",\"type\":\"Edm.String\",\"searchable\":true,\"filterable\":true,\"retrievable\":true,\"sortable\":true,\"facetable\":true,\"key\":false,\"indexAnalyzer\":null,\"searchAnalyzer\":null,\"analyzer\":null,\"synonymMaps\":[]},{\"name\":\"feature_id\",\"type\":\"Edm.String\",\"searchable\":true,\"filterable\":false,\"retrievable\":true,\"sortable\":true,\"facetable\":true,\"key\":true,\"indexAnalyzer\":null,\"searchAnalyzer\":null,\"analyzer\":null,\"synonymMaps\":[]}],\"scoringProfiles\":[],\"corsOptions\":null,\"suggesters\":[],\"analyzers\":[],\"tokenizers\":[],\"tokenFilters\":[],\"charFilters\":[]}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Type" : "application/json; odata.metadata=minimal", + "Location" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexes('indexforindexers')?allowIndexDowntime=false&api-version=2019-05-06" + }, + "Exception" : null + }, { + "Method" : "PUT", + "Uri" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexers('i1')?api-version=2019-05-06", + "Headers" : { + "Content-Type" : "application/json; charset=utf-8" + }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "a7d0689d-7210-4f49-8ddb-4f0848d0f632", + "StatusCode" : "201", + "Date" : "Wed, 06 Nov 2019 23:23:34 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "ETag" : "W/\"0x8D76310580BABF1\"", + "elapsed-time" : "417", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "412", + "Body" : "{\"@odata.context\":\"https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/$metadata#indexers/$entity\",\"@odata.etag\":\"\\\"0x8D76310580BABF1\\\"\",\"name\":\"i1\",\"description\":null,\"dataSourceName\":\"azs-java-test-sql\",\"skillsetName\":null,\"targetIndexName\":\"indexforindexers\",\"disabled\":null,\"schedule\":{\"interval\":\"P1D\",\"startTime\":\"0001-01-01T00:00:00Z\"},\"parameters\":null,\"fieldMappings\":[],\"outputFieldMappings\":[]}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Type" : "application/json; odata.metadata=minimal", + "Location" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexers('i1')?api-version=2019-05-06" + }, + "Exception" : null + }, { + "Method" : "PUT", + "Uri" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexers('i2')?api-version=2019-05-06", + "Headers" : { + "Content-Type" : "application/json; charset=utf-8" + }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "746a9175-fef0-4b95-8506-6789667ea15b", + "StatusCode" : "201", + "Date" : "Wed, 06 Nov 2019 23:23:34 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "ETag" : "W/\"0x8D76310586B6C82\"", + "elapsed-time" : "558", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "412", + "Body" : "{\"@odata.context\":\"https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/$metadata#indexers/$entity\",\"@odata.etag\":\"\\\"0x8D76310586B6C82\\\"\",\"name\":\"i2\",\"description\":null,\"dataSourceName\":\"azs-java-test-sql\",\"skillsetName\":null,\"targetIndexName\":\"indexforindexers\",\"disabled\":null,\"schedule\":{\"interval\":\"P1D\",\"startTime\":\"0001-01-01T00:00:00Z\"},\"parameters\":null,\"fieldMappings\":[],\"outputFieldMappings\":[]}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Type" : "application/json; odata.metadata=minimal", + "Location" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexers('i2')?api-version=2019-05-06" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/indexers?api-version=2019-05-06", + "Headers" : { }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "fc8fcb5c-9a18-44a1-9cb4-1f4e544a556a", + "StatusCode" : "200", + "Date" : "Wed, 06 Nov 2019 23:23:34 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "elapsed-time" : "17", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "724", + "Body" : "{\"@odata.context\":\"https://azs-sdkf6c1963413a7.search-dogfood.windows-int.net/$metadata#indexers\",\"value\":[{\"@odata.etag\":\"\\\"0x8D76310580BABF1\\\"\",\"name\":\"i1\",\"description\":null,\"dataSourceName\":\"azs-java-test-sql\",\"skillsetName\":null,\"targetIndexName\":\"indexforindexers\",\"disabled\":null,\"schedule\":{\"interval\":\"P1D\",\"startTime\":\"0001-01-01T00:00:00Z\"},\"parameters\":null,\"fieldMappings\":[],\"outputFieldMappings\":[]},{\"@odata.etag\":\"\\\"0x8D76310586B6C82\\\"\",\"name\":\"i2\",\"description\":null,\"dataSourceName\":\"azs-java-test-sql\",\"skillsetName\":null,\"targetIndexName\":\"indexforindexers\",\"disabled\":null,\"schedule\":{\"interval\":\"P1D\",\"startTime\":\"0001-01-01T00:00:00Z\"},\"parameters\":null,\"fieldMappings\":[],\"outputFieldMappings\":[]}]}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Type" : "application/json; odata.metadata=minimal" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/search/azure-search/src/test/resources/session-records/createIndexerFailsWithUsefulMessageOnUserError.json b/sdk/search/azure-search/src/test/resources/session-records/createIndexerFailsWithUsefulMessageOnUserError.json new file mode 100644 index 0000000000000..9e6daa422b2ab --- /dev/null +++ b/sdk/search/azure-search/src/test/resources/session-records/createIndexerFailsWithUsefulMessageOnUserError.json @@ -0,0 +1,28 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://azs-sdka862284357a2.search-dogfood.windows-int.net/indexers('indexer')?api-version=2019-05-06", + "Headers" : { + "Content-Type" : "application/json; charset=utf-8" + }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "95c2e372-cbfd-444f-942a-a8a2315c7d54", + "StatusCode" : "400", + "Date" : "Wed, 06 Nov 2019 23:23:19 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "elapsed-time" : "47", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "118", + "Body" : "{\"error\":{\"code\":\"\",\"message\":\"This indexer refers to a data source 'thisdatasourcedoesnotexist' that doesn't exist\"}}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Language" : "en", + "Content-Type" : "application/json; odata.metadata=minimal" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/search/azure-search/src/test/resources/session-records/createIndexerReturnsCorrectDefinition.json b/sdk/search/azure-search/src/test/resources/session-records/createIndexerReturnsCorrectDefinition.json new file mode 100644 index 0000000000000..76b254348d55a --- /dev/null +++ b/sdk/search/azure-search/src/test/resources/session-records/createIndexerReturnsCorrectDefinition.json @@ -0,0 +1,29 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://azs-sdkb0366826ab7b.search-dogfood.windows-int.net/indexers('indexer')?api-version=2019-05-06", + "Headers" : { + "Content-Type" : "application/json; charset=utf-8" + }, + "Response" : { + "Pragma" : "no-cache", + "retry-after" : "0", + "request-id" : "35f99075-fe96-4d5e-ab86-3d91b21ab649", + "StatusCode" : "201", + "Date" : "Wed, 06 Nov 2019 23:22:17 GMT", + "Strict-Transport-Security" : "max-age=15724800; includeSubDomains", + "Cache-Control" : "no-cache", + "ETag" : "W/\"0x8D763102A1D176A\"", + "elapsed-time" : "265", + "OData-Version" : "4.0", + "Expires" : "-1", + "Content-Length" : "520", + "Body" : "{\"@odata.context\":\"https://azs-sdkb0366826ab7b.search-dogfood.windows-int.net/$metadata#indexers/$entity\",\"@odata.etag\":\"\\\"0x8D763102A1D176A\\\"\",\"name\":\"indexer\",\"description\":null,\"dataSourceName\":\"azs-java-test-sql\",\"skillsetName\":null,\"targetIndexName\":\"indexforindexers\",\"disabled\":true,\"schedule\":{\"interval\":\"P1D\",\"startTime\":\"0001-01-01T00:00:00Z\"},\"parameters\":{\"batchSize\":50,\"maxFailedItems\":10,\"maxFailedItemsPerBatch\":10,\"base64EncodeKeys\":null,\"configuration\":{}},\"fieldMappings\":[],\"outputFieldMappings\":[]}", + "Preference-Applied" : "odata.include-annotations=\"*\"", + "Content-Type" : "application/json; odata.metadata=minimal", + "Location" : "https://azs-sdkb0366826ab7b.search-dogfood.windows-int.net/indexers('indexer')?api-version=2019-05-06" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file