diff --git a/sdk/search/search-documents/README.md b/sdk/search/search-documents/README.md index b0fa601a53b5..631b42543051 100644 --- a/sdk/search/search-documents/README.md +++ b/sdk/search/search-documents/README.md @@ -529,6 +529,26 @@ async function main() { main(); ``` +#### Get a list of existing indexers in the service +```js +const { SearchServiceClient, AzureKeyCredential } = require("@azure/search-documents"); + +const client = new SearchServiceClient("", new AzureKeyCredential("")); + +async function main() { + let listOfIndexers = await client.listIndexers(); + + for(let indexer of listOfIndexers) { + console.log(`Name: ${indexer.name}`); + console.log(`Datasource Name: ${indexer.dataSourceName}`); + console.log(`Skillset Name: ${indexer.skillsetName}`); + console.log(); + } +} + +main(); +``` + #### Create an Index ```js @@ -719,6 +739,32 @@ async function main() { main(); ``` +#### Create an Indexer +```js +const { SearchServiceClient, AzureKeyCredential } = require("@azure/search-documents"); + +const client = new SearchServiceClient("", new AzureKeyCredential("")); + +async function main() { + const indexer = await client.createIndexer({ + name: 'my-azure-indexer-1', + description: "My Azure Indexer 1", + dataSourceName: "testblobstoragesjama", + targetIndexName: "azureblob-index-2", + isDisabled: false, + fieldMappings: [{ + sourceFieldName: "metadata_storage_path", + targetFieldName: "metadata_storage_path", + mappingFunction: { + name: "base64Encode" + } + }] + }); +} + +main(); +``` + #### Create a SynonymMap ```js @@ -736,6 +782,57 @@ async function main() { main(); ``` +#### Retrieve an existing indexer and modify a field in it +```js +const { SearchServiceClient, AzureKeyCredential } = require("@azure/search-documents"); + +const client = new SearchServiceClient("", new AzureKeyCredential("")); + +async function main() { + const indexer = await client.getIndexer("my-azure-indexer-1"); + indexer.isDisabled = true; + indexer = await client.createOrUpdateIndexer(indexer); + + console.log(`Name: ${indexer.name}`); + console.log(`Description: ${indexer.description}`); + console.log(`Datasource Name: ${indexer.dataSourceName}`); + console.log(`Target Index Name: ${indexer.targetIndexName}`); + console.log(`IsDisabled: ${indexer.isDisabled}`); + console.log(`Field Mappings`); + for(let fieldMapping of indexer.fieldMappings) { + console.log(`\tSource Field Name: ${fieldMapping.sourceFieldName}`); + console.log(`\tTarget Field Name: ${fieldMapping.targetFieldName}`); + console.log(`\tMapping Function Name: ${fieldMapping.mappingFunction.name}`); + } +} + + + +main(); +``` + +### Get the status of an indexer +```js +const { SearchServiceClient, AzureKeyCredential } = require("@azure/search-documents"); + +const client = new SearchServiceClient("", new AzureKeyCredential("")); + +async function main() { + const indexerStatus = await client.getIndexerStatus('my-azure-indexer-1'); + console.log(`Name: ${indexerStatus.name}`); + console.log(`OData Context: ${indexerStatus["@odata.context"]}`); + console.log(`Status: ${indexerStatus.status}`); + console.log(`Execution History`); + for(let execution of indexerStatus.executionHistory) { + console.log(`\tStatus: ${execution.status}`); + console.log(`\tFinal Tracking State: ${execution.finalTrackingState}`); + console.log(`\tInitial Tracking State: ${execution.initialTrackingState}`); + } +} + +main(); +``` + ## Troubleshooting ### Enable logs diff --git a/sdk/search/search-documents/review/search-documents.api.md b/sdk/search/search-documents/review/search-documents.api.md index 26c2065c80a4..f981197cb4b7 100644 --- a/sdk/search/search-documents/review/search-documents.api.md +++ b/sdk/search/search-documents/review/search-documents.api.md @@ -154,9 +154,15 @@ export interface CorsOptions { // @public export type CountDocumentsOptions = OperationOptions; +// @public +export type CreateIndexerOptions = OperationOptions; + // @public export type CreateIndexOptions = OperationOptions; +// @public +export type CreateorUpdateIndexerOptions = OperationOptions & ETagOperationOptions; + // @public export interface CreateOrUpdateIndexOptions extends OperationOptions, ETagOperationOptions { allowIndexDowntime?: boolean; @@ -193,6 +199,9 @@ export interface DefaultCognitiveServicesAccount { // @public export type DeleteDocumentsOptions = IndexDocuments; +// @public +export type DeleteIndexerOptions = OperationOptions & ETagOperationOptions; + // @public export type DeleteIndexOptions = OperationOptions & ETagOperationOptions; @@ -307,6 +316,21 @@ export interface FacetResult { // @public export type Field = SimpleField | ComplexField; +// @public +export interface FieldMapping { + mappingFunction?: FieldMappingFunction; + sourceFieldName: string; + targetFieldName?: string; +} + +// @public +export interface FieldMappingFunction { + name: string; + parameters?: { + [propertyName: string]: any; + }; +} + // @public export interface FreshnessScoringFunction { boost: number; @@ -334,6 +358,12 @@ export interface GetDocumentOptions extends OperationOptions { selectedFields?: Fields[]; } +// @public +export type GetIndexerOptions = OperationOptions; + +// @public +export type GetIndexerStatusOptions = OperationOptions; + // @public export type GetIndexOptions = OperationOptions; @@ -415,6 +445,66 @@ export interface IndexDocumentsResult { readonly results: IndexingResult[]; } +// @public +export interface Indexer { + dataSourceName: string; + description?: string; + etag?: string; + fieldMappings?: FieldMapping[]; + isDisabled?: boolean; + name: string; + outputFieldMappings?: FieldMapping[]; + parameters?: IndexingParameters; + schedule?: IndexingSchedule; + skillsetName?: string; + targetIndexName: string; +} + +// @public +export interface IndexerExecutionInfo { + readonly executionHistory: IndexerExecutionResult[]; + readonly lastResult?: IndexerExecutionResult; + readonly limits: IndexerLimits; + readonly status: IndexerStatus; +} + +// @public +export interface IndexerExecutionResult { + readonly endTime?: Date; + readonly errorMessage?: string; + readonly errors: ItemError[]; + readonly failedItemCount: number; + readonly finalTrackingState?: string; + readonly initialTrackingState?: string; + readonly itemCount: number; + readonly startTime?: Date; + readonly status: IndexerExecutionStatus; + readonly warnings: ItemWarning[]; +} + +// @public +export type IndexerExecutionStatus = 'transientFailure' | 'success' | 'inProgress' | 'reset'; + +// @public +export interface IndexerLimits { + readonly maxDocumentContentCharactersToExtract?: number; + readonly maxDocumentExtractionSize?: number; + readonly maxRunTime?: string; +} + +// @public +export type IndexerStatus = 'unknown' | 'error' | 'running'; + +// @public +export interface IndexingParameters { + batchSize?: number; + configuration?: { + [propertyName: string]: any; + }; + maxFailedItems?: number; + maxFailedItemsPerBatch?: number; +} + // @public export interface IndexingResult { readonly errorMessage?: string; @@ -423,6 +513,12 @@ export interface IndexingResult { readonly succeeded: boolean; } +// @public +export interface IndexingSchedule { + interval: string; + startTime?: Date; +} + // @public export interface InputFieldMappingEntry { inputs?: InputFieldMappingEntry[]; @@ -431,6 +527,25 @@ export interface InputFieldMappingEntry { sourceContext?: string; } +// @public +export interface ItemError { + readonly details?: string; + readonly documentationLink?: string; + readonly errorMessage: string; + readonly key?: string; + readonly name?: string; + readonly statusCode: number; +} + +// @public +export interface ItemWarning { + readonly details?: string; + readonly documentationLink?: string; + readonly key?: string; + readonly message: string; + readonly name?: string; +} + // @public export interface KeepTokenFilter { keepWords: string[]; @@ -660,8 +775,13 @@ export interface LimitTokenFilter { } // @public -export interface ListIndexesOptions extends OperationOptions { - select?: string[]; +export interface ListIndexersOptions extends OperationOptions { + select?: Fields[]; +} + +// @public +export interface ListIndexesOptions extends OperationOptions { + select?: Fields[]; } // @public @@ -673,8 +793,8 @@ export interface ListSearchResultsPageSettings { export type ListSkillsetsOptions = OperationOptions; // @public -export interface ListSynonymMapsOptions extends OperationOptions { - select?: string[]; +export interface ListSynonymMapsOptions extends OperationOptions { + select?: Fields[]; } // @public @@ -884,6 +1004,12 @@ export interface RawSearchRequest { // @public export type RegexFlags = 'CANON_EQ' | 'CASE_INSENSITIVE' | 'COMMENTS' | 'DOTALL' | 'LITERAL' | 'MULTILINE' | 'UNICODE_CASE' | 'UNIX_LINES'; +// @public +export type ResetIndexerOptions = OperationOptions; + +// @public +export type RunIndexerOptions = OperationOptions; + // @public export type ScoringFunction = DistanceScoringFunction | FreshnessScoringFunction | MagnitudeScoringFunction | TagScoringFunction; @@ -986,22 +1112,30 @@ export class SearchServiceClient { analyzeText(indexName: string, options: AnalyzeTextOptions): Promise; readonly apiVersion: string; createIndex(index: Index, options?: CreateIndexOptions): Promise; + createIndexer(indexer: Indexer, options?: CreateIndexerOptions): Promise; createOrUpdateIndex(index: Index, options?: CreateOrUpdateIndexOptions): Promise; + createOrUpdateIndexer(indexer: Indexer, options?: CreateorUpdateIndexerOptions): Promise; createOrUpdateSkillset(skillset: Skillset, options?: CreateOrUpdateSkillsetOptions): Promise; createOrUpdateSynonymMap(synonymMap: SynonymMap, options?: CreateOrUpdateSynonymMapOptions): Promise; createSkillset(skillset: Skillset, options?: CreateSkillsetOptions): Promise; createSynonymMap(synonymMap: SynonymMap, options?: CreateSynonymMapOptions): Promise; deleteIndex(indexName: string, options?: DeleteIndexOptions): Promise; + deleteIndexer(indexerName: string, options?: DeleteIndexerOptions): Promise; deleteSkillset(skillsetName: string, options?: DeleteSkillsetOptions): Promise; deleteSynonymMap(synonymMapName: string, options?: DeleteSynonymMapOptions): Promise; readonly endpoint: string; getIndex(indexName: string, options?: GetIndexOptions): Promise; + getIndexer(indexerName: string, options?: GetIndexerOptions): Promise; + getIndexerStatus(indexerName: string, options?: GetIndexerStatusOptions): Promise; getIndexStatistics(indexName: string, options?: GetIndexStatisticsOptions): Promise; getSkillset(skillsetName: string, options?: GetSkillSetOptions): Promise; getSynonymMap(synonymMapName: string, options?: GetSynonymMapsOptions): Promise; - listIndexes(options?: ListIndexesOptions): Promise; + listIndexers(options?: ListIndexersOptions): Promise>>; + listIndexes(options?: ListIndexesOptions): Promise>>; listSkillsets(options?: ListSkillsetsOptions): Promise; - listSynonymMaps(options?: ListSynonymMapsOptions): Promise; + listSynonymMaps(options?: ListSynonymMapsOptions): Promise>>; + resetIndexer(indexerName: string, options?: ResetIndexerOptions): Promise; + runIndexer(indexerName: string, options?: RunIndexerOptions): Promise; } // @public diff --git a/sdk/search/search-documents/src/index.ts b/sdk/search/search-documents/src/index.ts index dab5ef29ff5d..4f1043f30b0f 100644 --- a/sdk/search/search-documents/src/index.ts +++ b/sdk/search/search-documents/src/index.ts @@ -67,7 +67,15 @@ export { ComplexDataType, CognitiveServicesAccount, Skill, - SynonymMap + SynonymMap, + ListIndexersOptions, + CreateIndexerOptions, + GetIndexerOptions, + CreateorUpdateIndexerOptions, + DeleteIndexerOptions, + GetIndexerStatusOptions, + ResetIndexerOptions, + RunIndexerOptions } from "./serviceModels"; export { default as GeographyPoint } from "./geographyPoint"; export { odata } from "./odata"; @@ -183,6 +191,18 @@ export { VisualFeature, KeyPhraseExtractionSkillLanguage, OcrSkillLanguage, - TextExtractionAlgorithm + TextExtractionAlgorithm, + Indexer, + FieldMapping, + IndexingParameters, + IndexingSchedule, + FieldMappingFunction, + IndexerExecutionInfo, + IndexerExecutionResult, + IndexerLimits, + IndexerStatus, + ItemError, + IndexerExecutionStatus, + ItemWarning } from "./generated/service/models"; export { AzureKeyCredential } from "@azure/core-auth"; diff --git a/sdk/search/search-documents/src/searchServiceClient.ts b/sdk/search/search-documents/src/searchServiceClient.ts index 977c2c2e6ef0..4da59b784913 100644 --- a/sdk/search/search-documents/src/searchServiceClient.ts +++ b/sdk/search/search-documents/src/searchServiceClient.ts @@ -11,7 +11,12 @@ import { } from "@azure/core-http"; import { CanonicalCode } from "@opentelemetry/types"; import { SDK_VERSION } from "./constants"; -import { AnalyzeResult, GetIndexStatisticsResult } from "./generated/service/models"; +import { + AnalyzeResult, + GetIndexStatisticsResult, + Indexer, + IndexerExecutionInfo +} from "./generated/service/models"; import { SearchServiceClient as GeneratedClient } from "./generated/service/searchServiceClient"; import { logger } from "./logger"; import { createSearchApiKeyCredentialPolicy } from "./searchApiKeyCredentialPolicy"; @@ -35,7 +40,15 @@ import { ListSkillsetsOptions, ListSynonymMapsOptions, Skillset, - SynonymMap + SynonymMap, + ListIndexersOptions, + CreateIndexerOptions, + GetIndexerOptions, + CreateorUpdateIndexerOptions, + DeleteIndexerOptions, + GetIndexerStatusOptions, + ResetIndexerOptions, + RunIndexerOptions } from "./serviceModels"; import * as utils from "./serviceUtils"; import { createSpan } from "./tracing"; @@ -140,7 +153,9 @@ export class SearchServiceClient { * Retrieves a list of existing indexes in the service. * @param options Options to the list index operation. */ - public async listIndexes(options: ListIndexesOptions = {}): Promise { + public async listIndexes( + options: ListIndexesOptions = {} + ): Promise>> { const { span, updatedOptions } = createSpan("SearchServiceClient-listIndexes", options); try { const result = await this.client.indexes.list({ @@ -185,7 +200,9 @@ export class SearchServiceClient { * Retrieves a list of existing SynonymMaps in the service. * @param options Options to the list SynonymMaps operation. */ - public async listSynonymMaps(options: ListSynonymMapsOptions = {}): Promise { + public async listSynonymMaps( + options: ListSynonymMapsOptions = {} + ): Promise>> { const { span, updatedOptions } = createSpan("SearchServiceClient-listSynonymMaps", options); try { const result = await this.client.synonymMaps.list({ @@ -204,6 +221,31 @@ export class SearchServiceClient { } } + /** + * Retrieves a list of existing indexers in the service. + * @param options Options to the list indexers operation. + */ + public async listIndexers( + options: ListIndexersOptions = {} + ): Promise>> { + const { span, updatedOptions } = createSpan("SearchServiceClient-listIndexers", options); + try { + const result = await this.client.indexers.list({ + ...operationOptionsToRequestOptionsBase(updatedOptions), + select: updatedOptions.select?.join(",") + }); + return result.indexers; + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Retrieves information about an index. * @param indexName The name of the index. @@ -256,7 +298,7 @@ export class SearchServiceClient { } /** - * Retrieves information about an SynonymMap. + * Retrieves information about a SynonymMap. * @param indexName The name of the Skillset. * @param options Additional optional arguments. */ @@ -282,6 +324,30 @@ export class SearchServiceClient { } } + /** + * Retrieves information about an Indexer. + * @param indexerName The name of the Indexer. + * @param options Additional optional arguments. + */ + public async getIndexer(indexerName: string, options: GetIndexerOptions = {}): Promise { + const { span, updatedOptions } = createSpan("SearchServiceClient-getIndexer", options); + try { + const result = await this.client.indexers.get( + indexerName, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Creates a new index. * @param index The information describing the index to be created. @@ -360,6 +426,33 @@ export class SearchServiceClient { } } + /** + * Creates a new indexer in a search service. + * @param indexer The indexer definition to create in a search service. + * @param options Additional optional arguments. + */ + public async createIndexer( + indexer: Indexer, + options: CreateIndexerOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("SearchServiceClient-createIndexer", options); + try { + const result = await this.client.indexers.create( + indexer, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Creates a new index or modifies an existing one. * @param index The information describing the index to be created. @@ -450,6 +543,37 @@ export class SearchServiceClient { } } + /** + * Creates a new indexer or modifies an existing one. + * @param indexer The information describing the indexer to be created/updated. + * @param options Additional optional arguments. + */ + public async createOrUpdateIndexer( + indexer: Indexer, + options: CreateorUpdateIndexerOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan( + "SearchServiceClient-createOrUpdateIndexer", + options + ); + try { + const result = await this.client.indexers.createOrUpdate( + indexer.name, + indexer, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Deletes an existing index. * @param indexName The name of the index to delete. @@ -525,6 +649,32 @@ export class SearchServiceClient { } } + /** + * Deletes an existing indexer. + * @param indexerName The name of the indexer to delete. + * @param options Additional optional arguments. + */ + public async deleteIndexer( + indexerName: string, + options: DeleteIndexerOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("SearchServiceClient-deleteIndexer", options); + try { + await this.client.indexers.deleteMethod( + indexerName, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Retrieves statistics about an index, such as the count of documents and the size * of index storage. @@ -553,6 +703,79 @@ export class SearchServiceClient { } } + /** + * Returns the current status and execution history of an indexer. + * @param indexerName The name of the indexer. + * @param options Additional optional arguments. + */ + public async getIndexerStatus( + indexerName: string, + options: GetIndexerStatusOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("SearchServiceClient-getIndexerStatus", options); + try { + const result = await this.client.indexers.getStatus( + indexerName, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + + /** + * Resets the change tracking state associated with an indexer. + * @param indexerName The name of the indexer to reset. + * @param options Additional optional arguments. + */ + public async resetIndexer(indexerName: string, options: ResetIndexerOptions = {}): Promise { + const { span, updatedOptions } = createSpan("SearchServiceClient-resetIndexer", options); + try { + await this.client.indexers.reset( + indexerName, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + + /** + * Runs an indexer on-demand. + * @param indexerName The name of the indexer to run. + * @param options Additional optional arguments. + */ + public async runIndexer(indexerName: string, options: RunIndexerOptions = {}): Promise { + const { span, updatedOptions } = createSpan("SearchServiceClient-runIndexer", options); + try { + await this.client.indexers.run( + indexerName, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Calls an analyzer or tokenizer manually on provided text. * @param indexName The name of the index that contains the field to analyze diff --git a/sdk/search/search-documents/src/serviceModels.ts b/sdk/search/search-documents/src/serviceModels.ts index 300eef1f9029..abe42ca06f07 100644 --- a/sdk/search/search-documents/src/serviceModels.ts +++ b/sdk/search/search-documents/src/serviceModels.ts @@ -80,25 +80,36 @@ export type ListSkillsetsOptions = OperationOptions; /** * Options for a list synonymMaps operation. */ -export interface ListSynonymMapsOptions extends OperationOptions { +export interface ListSynonymMapsOptions extends OperationOptions { /** * Selects which top-level properties of the synonym maps to retrieve. Specified as a * comma-separated list of JSON property names, or '*' for all properties. The default is all * properties. */ - select?: string[]; + select?: Fields[]; } /** * Options for a list indexes operation. */ -export interface ListIndexesOptions extends OperationOptions { +export interface ListIndexesOptions extends OperationOptions { /** * 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. */ - select?: string[]; + select?: Fields[]; +} +/** + * Options for a list indexers operation. + */ +export interface ListIndexersOptions extends OperationOptions { + /** + * 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. + */ + select?: Fields[]; } /** @@ -116,11 +127,31 @@ export type GetSkillSetOptions = OperationOptions; */ export type GetSynonymMapsOptions = OperationOptions; +/** + * Options for get indexer operation. + */ +export type GetIndexerOptions = OperationOptions; + /** * Options for get index statistics operation. */ export type GetIndexStatisticsOptions = OperationOptions; +/** + * Options for get indexer status operation. + */ +export type GetIndexerStatusOptions = OperationOptions; + +/** + * Options for reset indexer operation. + */ +export type ResetIndexerOptions = OperationOptions; + +/** + * Options for run indexer operation. + */ +export type RunIndexerOptions = OperationOptions; + /** * Options for create index operation. */ @@ -136,6 +167,11 @@ export type CreateSkillsetOptions = OperationOptions; */ export type CreateSynonymMapOptions = OperationOptions; +/** + * Options for create indexer operation. + */ +export type CreateIndexerOptions = OperationOptions; + /** * Options for all operations with etag parameters. */ @@ -169,6 +205,11 @@ export type CreateOrUpdateSkillsetOptions = OperationOptions & ETagOperationOpti */ export type CreateOrUpdateSynonymMapOptions = OperationOptions & ETagOperationOptions; +/** + * Options for create/update indexer operation. + */ +export type CreateorUpdateIndexerOptions = OperationOptions & ETagOperationOptions; + /** * Options for delete index operation. */ @@ -184,6 +225,11 @@ export type DeleteSkillsetOptions = OperationOptions & ETagOperationOptions; */ export type DeleteSynonymMapOptions = OperationOptions & ETagOperationOptions; +/** + * Options for delete indexer operation. + */ +export type DeleteIndexerOptions = OperationOptions & ETagOperationOptions; + /** * Options for analyze text operation. */ diff --git a/sdk/search/search-documents/src/serviceUtils.ts b/sdk/search/search-documents/src/serviceUtils.ts index e96f1f1743d5..6f51b264704e 100644 --- a/sdk/search/search-documents/src/serviceUtils.ts +++ b/sdk/search/search-documents/src/serviceUtils.ts @@ -266,12 +266,18 @@ export function publicSkillsetToGeneratedSkillset(skillset: Skillset): Generated } export function generatedSynonymMapToPublicSynonymMap(synonymMap: GeneratedSynonymMap): SynonymMap { - return { + let result: SynonymMap = { name: synonymMap.name, encryptionKey: synonymMap.encryptionKey, etag: synonymMap.etag, - synonyms: synonymMap.synonyms.split("\n") + synonyms: [] }; + + if (synonymMap.synonyms) { + result.synonyms = synonymMap.synonyms.split("\n"); + } + + return result; } export function publicSynonymMapToGeneratedSynonymMap(synonymMap: SynonymMap): GeneratedSynonymMap {