├── .github ├── CODEOWNERS └── workflows │ └── test.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE.md ├── clean.ts ├── eslint.config.mjs ├── karma-gh-actions.conf.js ├── karma.conf.js ├── lib ├── client │ ├── delivery-client.factory.ts │ ├── delivery-client.ts │ ├── idelivery-client.interface.ts │ └── index.ts ├── config │ ├── delivery-configs.ts │ └── index.ts ├── contracts │ ├── contracts.ts │ └── index.ts ├── elements │ ├── element-models.ts │ ├── element-resolver.ts │ ├── element-type.ts │ ├── elements.ts │ └── index.ts ├── images │ ├── image-url-transformation-builder.factory.ts │ ├── image-url-transformation-builder.ts │ ├── image.models.ts │ └── index.ts ├── index.ts ├── mappers │ ├── element.mapper.ts │ ├── generic-element.mapper.ts │ ├── index.ts │ ├── item.mapper.ts │ ├── language.mapper.ts │ ├── sync.mapper.ts │ ├── taxonomy.mapper.ts │ ├── type.mapper.ts │ └── used-in.mapper.ts ├── models │ ├── common │ │ ├── base-responses.ts │ │ ├── common-models.ts │ │ ├── filters.ts │ │ ├── headers.ts │ │ ├── index.ts │ │ ├── pagination.class.ts │ │ ├── parameters.ts │ │ └── sort-order.ts │ ├── content-type-models.ts │ ├── element-models.ts │ ├── index.ts │ ├── item-models.ts │ ├── language-models.ts │ ├── responses.ts │ ├── sync-models.ts │ └── taxonomy-models.ts ├── query │ ├── common │ │ ├── base-item-listing-query.class.ts │ │ ├── base-listing-query.class.ts │ │ └── base-query.class.ts │ ├── element │ │ └── element-query.class.ts │ ├── index.ts │ ├── item │ │ ├── multiple-items-query.class.ts │ │ └── single-item-query.class.ts │ ├── items-feed │ │ └── items-feed-query.class.ts │ ├── language │ │ └── languages-query.class.ts │ ├── sync │ │ ├── initialize-sync-query.class.ts │ │ └── sync-changes-query.class.ts │ ├── taxonomy │ │ ├── taxonomies-query.class.ts │ │ └── taxonomy-query.class.ts │ ├── type │ │ ├── multiple-type-query.class.ts │ │ └── single-type-query.class.ts │ └── used-in │ │ └── used-in-query.class.ts ├── sdk-info.generated.ts ├── services │ ├── base-delivery-query.service.ts │ ├── delivery-query.service.ts │ ├── index.ts │ └── mapping.service.ts └── utilities │ ├── codename.helper.ts │ ├── delivery-url.helper.ts │ ├── enum.helper.ts │ └── index.ts ├── misc ├── set-sdk-version.ts ├── verify-sdk-version.ts └── version-helper.ts ├── package-lock.json ├── package.json ├── readme.md ├── test ├── browser │ ├── client │ │ └── delivery-client.spec.ts │ ├── isolated-tests │ │ ├── core │ │ │ ├── base-url.spec.ts │ │ │ ├── core-headers.spec.ts │ │ │ ├── global-headers.spec.ts │ │ │ ├── global-query-headers.spec.ts │ │ │ ├── preview-headers.spec.ts │ │ │ ├── preview-url.spec.ts │ │ │ ├── preview-with-secured-mode.spec.ts │ │ │ ├── proxy-url-spec.ts │ │ │ ├── secured-api-headers.spec.ts │ │ │ └── wait-for-loading-new-content-header.spec.ts │ │ ├── elements │ │ │ ├── assets │ │ │ │ ├── asset-rendition.spec.json │ │ │ │ ├── asset-rendition.spec.ts │ │ │ │ └── custom-assets-domain.spec.ts │ │ │ ├── custom-element │ │ │ │ ├── custom-element-with-custom-model.spec.ts │ │ │ │ ├── custom-element.spec.json │ │ │ │ └── custom-element.spec.ts │ │ │ ├── rich-text │ │ │ │ ├── rich-text.spec.json │ │ │ │ └── rich-text.spec.ts │ │ │ └── unknown-element │ │ │ │ ├── unknown-element.spec.json │ │ │ │ └── unknown-element.spec.ts │ │ ├── endpoints │ │ │ ├── items-feed │ │ │ │ ├── items-feed-all.spec.json │ │ │ │ ├── items-feed-all.spec.ts │ │ │ │ ├── items-feed.spec.json │ │ │ │ └── items-feed.spec.ts │ │ │ ├── items │ │ │ │ ├── circular-referenced-linked-items-mapping.spec.json │ │ │ │ ├── circular-referenced-linked-items-mapping.spec.ts │ │ │ │ ├── items-list-response.spec.json │ │ │ │ ├── items-list-response.spec.ts │ │ │ │ ├── items-list-with-reserved-names-in-linked-items-response.spec.json │ │ │ │ ├── items-list-with-reserved-names-in-linked-items-response.spec.ts │ │ │ │ ├── items-list-with-reserved-names-response.spec.json │ │ │ │ ├── items-list-with-reserved-names-response.spec.ts │ │ │ │ ├── items-with-total-count.spec.json │ │ │ │ ├── items-with-total-count.spec.ts │ │ │ │ ├── items-without-total-count.spec.json │ │ │ │ └── items-without-total-count.spec.ts │ │ │ ├── languages │ │ │ │ ├── list-languages.spec.json │ │ │ │ └── list-languages.spec.ts │ │ │ ├── sync │ │ │ │ ├── initialize-sync.spec.json │ │ │ │ ├── initialize-sync.spec.ts │ │ │ │ ├── sync-changes.spec.json │ │ │ │ └── sync-changes.spec.ts │ │ │ └── used-in │ │ │ │ ├── used-in-all.spec.json │ │ │ │ ├── used-in-all.spec.ts │ │ │ │ ├── used-in.spec.json │ │ │ │ └── used-in.spec.ts │ │ ├── fake-data │ │ │ ├── fake-warrior-response.json │ │ │ └── fake-warrior-without-modular-data-response.json │ │ ├── http │ │ │ └── custom-http-service.spec.ts │ │ ├── images │ │ │ └── image-url-builder.spec.ts │ │ ├── query │ │ │ ├── custom-headers.spec.ts │ │ │ ├── query-config.spec.ts │ │ │ ├── query-custom-parameter.spec.ts │ │ │ ├── query-initialization.spec.ts │ │ │ ├── query-url-debug.spec.ts │ │ │ └── wait-for-loading-new-content.spec.ts │ │ ├── response │ │ │ ├── item.spec.ts │ │ │ └── linked-items-mapping │ │ │ │ ├── linked-items-mapping-disabled.spec.ts │ │ │ │ ├── linked-items-mapping-enabled.spec.ts │ │ │ │ └── linked-items-mapping.json │ │ ├── services │ │ │ ├── generic-element-mapper.spec.ts │ │ │ ├── mapping-service.spec.json │ │ │ ├── mapping-service.spec.ts │ │ │ ├── taxonomy-mapper.spec.ts │ │ │ └── type-mapper.spec.ts │ │ └── url │ │ │ ├── item-filters.ts │ │ │ ├── item-url-parameters.spec.ts │ │ │ ├── item-url.spec.ts │ │ │ ├── taxonomies-url.spec.ts │ │ │ └── type-url.spec.ts │ ├── live-tests │ │ ├── custom-url │ │ │ └── custom-url.spec.ts │ │ ├── element │ │ │ └── live-element.spec.ts │ │ ├── errors │ │ │ └── kontent-error.spec.ts │ │ ├── item │ │ │ ├── live-item-base-linked-items.spec.ts │ │ │ └── live-item.spec.ts │ │ ├── items-feed │ │ │ ├── live-items-feed-all.spec.ts │ │ │ └── live-items-feed.spec.ts │ │ ├── items │ │ │ ├── live-items-all.spec.ts │ │ │ └── live-items.spec.ts │ │ ├── localization │ │ │ ├── localization-default-language.spec.ts │ │ │ └── localized-item.spec.ts │ │ ├── mapping │ │ │ └── live-mapping.service.spec.ts │ │ ├── network-response │ │ │ └── network-response.spec.ts │ │ ├── pagination │ │ │ └── pagination.spec.ts │ │ ├── promise │ │ │ └── response-by-promise.spec.ts │ │ ├── readme.md │ │ ├── taxonomy │ │ │ ├── live-taxonomies.spec.ts │ │ │ └── live-taxonomy.spec.ts │ │ └── types │ │ │ ├── live-type.spec.ts │ │ │ └── live-types.spec.ts │ └── setup │ │ ├── context.ts │ │ ├── delivery-test-client.ts │ │ ├── index.ts │ │ ├── models.ts │ │ └── setup.ts └── node │ └── base │ └── base-node-test.js ├── tsconfig.json ├── tsconfig.prod.json ├── tsconfig.webpack.json └── webpack.config.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths. 2 | # See https://help.github.com/articles/about-code-owners/ 3 | 4 | * @IvanKiral @Enngage @kontent-ai/javascript-maintainers 5 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [pull_request] 3 | jobs: 4 | build: 5 | runs-on: windows-latest 6 | strategy: 7 | matrix: 8 | node-version: [14.x] 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Use Node.js ${{ matrix.node-version }} 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: ${{ matrix.node-version }} 15 | - run: npm i 16 | - run: npm run test:all 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | .editorconfig 3 | 4 | # See http://help.github.com/ignore-files/ for more about ignoring files. 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /coverage 10 | /out-tsc 11 | 12 | # dependencies 13 | /node_modules 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | 31 | # misc 32 | /.sass-cache 33 | /connect.lock 34 | /coverage 35 | /libpeerconnection.log 36 | npm-debug.log 37 | testem.log 38 | /typings 39 | 40 | # e2e 41 | /e2e/*.js 42 | /e2e/*.map 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | * 3 | 4 | # But descend into directories 5 | 6 | # Recursively allow files under subtree 7 | !/lib/** 8 | !/dist/** 9 | !package.json 10 | !.npmignore 11 | !README.md 12 | !LICENSE.md 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "proseWrap": "always", 5 | "tabWidth": 4, 6 | "requireConfig": false, 7 | "useTabs": false, 8 | "trailingComma": "none", 9 | "bracketSpacing": true, 10 | "semi": true 11 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib", 3 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kontent s.r.o. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /clean.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, rmSync } from 'fs'; 2 | import { yellow } from 'colors'; 3 | 4 | const paths = ['dist']; 5 | for (const path of paths) { 6 | if (existsSync(path)) { 7 | rmSync(path, { recursive: true }); 8 | console.log(`Path '${yellow(path)}' has been deleted`); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js'; 2 | import tseslint from 'typescript-eslint'; 3 | 4 | export default tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, { 5 | languageOptions: { 6 | parserOptions: { 7 | project: true, 8 | tsconfigRootDir: import.meta.dirname 9 | } 10 | }, 11 | rules: { 12 | '@typescript-eslint/no-explicit-any': 'off', 13 | '@typescript-eslint/no-unused-vars': 'off', 14 | '@typescript-eslint/no-namespace': 'off', 15 | '@typescript-eslint/no-unsafe-member-access': 'off', 16 | '@typescript-eslint/no-unsafe-assignment': 'off', 17 | '@typescript-eslint/no-unsafe-argument': 'off', 18 | '@typescript-eslint/no-empty-object-type': 'off', 19 | '@typescript-eslint/restrict-template-expressions': 'off', 20 | '@typescript-eslint/no-unsafe-return': 'off', 21 | '@typescript-eslint/prefer-promise-reject-errors': 'off' 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /karma-gh-actions.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | frameworks: ["jasmine", "karma-typescript"], 4 | plugins: [ 5 | require("karma-jasmine"), 6 | require("karma-typescript"), 7 | require('karma-chrome-launcher'), 8 | require('karma-jasmine-html-reporter'), 9 | ], 10 | files: [ 11 | { pattern: "lib/**/*.ts" }, 12 | { pattern: "test/browser/**/*.ts" } 13 | ], 14 | exclude: [ 15 | ], 16 | preprocessors: { 17 | "lib/**/*.ts": ["karma-typescript"], 18 | "test/browser/**/*.ts": ["karma-typescript"] 19 | }, 20 | reporters: ["kjhtml", "progress", "karma-typescript"], 21 | browsers: ["Chrome"], 22 | karmaTypescriptConfig: { 23 | compilerOptions: { 24 | emitDecoratorMetadata: true, 25 | experimentalDecorators: true, 26 | resolveJsonModule: true, 27 | module: "commonjs", 28 | sourceMap: true, 29 | target: "ES5" 30 | }, 31 | exclude: ["node_modules"], 32 | bundlerOptions: { 33 | transforms: [ 34 | require("karma-typescript-es6-transform")() 35 | ] 36 | } 37 | }, 38 | coverageReporter: { 39 | reporters: [ 40 | { type: 'html', subdir: 'html' }, 41 | { type: 'lcov', subdir: 'lcov' }, 42 | { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' } 43 | ] 44 | }, 45 | port: 9669, 46 | colors: true, 47 | autoWatch: false, 48 | singleRun: true, 49 | client: { 50 | clearContext: false // leave Jasmine Spec Runner output visible in browser 51 | }, 52 | logLevel: config.DEBUG, 53 | browserDisconnectTolerance: 2, 54 | browserNoActivityTimeout: 5000000 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | frameworks: ['jasmine', 'karma-typescript'], 4 | plugins: [ 5 | require('karma-jasmine'), 6 | require('karma-typescript'), 7 | require('karma-chrome-launcher'), 8 | require('karma-jasmine-html-reporter') 9 | ], 10 | files: [{ pattern: 'lib/**/*.ts' }, { pattern: 'test/browser/**/*.ts' }], 11 | exclude: [], 12 | preprocessors: { 13 | 'lib/**/*.ts': ['karma-typescript'], 14 | 'test/browser/**/*.ts': ['karma-typescript'] 15 | }, 16 | reporters: ['kjhtml', 'progress', 'karma-typescript'], 17 | browsers: ['Chrome'], 18 | karmaTypescriptConfig: { 19 | compilerOptions: { 20 | emitDecoratorMetadata: true, 21 | experimentalDecorators: true, 22 | resolveJsonModule: true, 23 | module: 'commonjs', 24 | sourceMap: true, 25 | target: 'ES5' 26 | }, 27 | exclude: ['node_modules'], 28 | bundlerOptions: { 29 | transforms: [require('karma-typescript-es6-transform')()] 30 | } 31 | }, 32 | coverageReporter: { 33 | reporters: [ 34 | { type: 'html', subdir: 'html' }, 35 | { type: 'lcov', subdir: 'lcov' }, 36 | { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' } 37 | ] 38 | }, 39 | port: 9669, 40 | colors: true, 41 | autoWatch: true, 42 | singleRun: false, 43 | client: { 44 | clearContext: false // leave Jasmine Spec Runner output visible in browser 45 | }, 46 | logLevel: config.DEBUG, 47 | browserDisconnectTolerance: 2, 48 | browserNoActivityTimeout: 5000000 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /lib/client/delivery-client.factory.ts: -------------------------------------------------------------------------------- 1 | import { ClientTypes } from '../models'; 2 | import { IDeliveryClientConfig } from '../config/delivery-configs'; 3 | import { DeliveryClient } from './delivery-client'; 4 | 5 | export function createDeliveryClient( 6 | config: IDeliveryClientConfig 7 | ): DeliveryClient { 8 | return new DeliveryClient(config); 9 | } 10 | -------------------------------------------------------------------------------- /lib/client/idelivery-client.interface.ts: -------------------------------------------------------------------------------- 1 | import { ClientTypes, IContentItem } from '../models'; 2 | import { 3 | ElementQuery, 4 | ItemsFeedQuery, 5 | MultipleItemsQuery, 6 | MultipleTypeQuery, 7 | SingleItemQuery, 8 | SingleTypeQuery, 9 | TaxonomiesQuery, 10 | TaxonomyQuery, 11 | LanguagesQuery, 12 | InitializeSyncQuery, 13 | SyncChangesQuery, 14 | UsedInQuery 15 | } from '../query'; 16 | import { IMappingService } from '../services'; 17 | 18 | export interface IDeliveryClient { 19 | /** 20 | * Mapping service - can be used to get strongly typed responses from json result 21 | */ 22 | mappingService: IMappingService; 23 | 24 | /** 25 | * Gets query for languages 26 | */ 27 | languages(): LanguagesQuery; 28 | 29 | /** 30 | * Gets query for multiple types 31 | */ 32 | types(): MultipleTypeQuery; 33 | 34 | /** 35 | * Gets query for single type 36 | * @param {string} typeCodename - Codename of the type to retrieve 37 | */ 38 | type(typeCodename: TClientTypes['contentTypeCodenames']): SingleTypeQuery; 39 | 40 | /** 41 | * Gets query for multiple items 42 | */ 43 | items(): MultipleItemsQuery< 44 | TClientTypes, 45 | TContentItem 46 | >; 47 | 48 | /** 49 | * Gets query for items feed. Executes single HTTP request only 50 | */ 51 | itemsFeed(): ItemsFeedQuery< 52 | TClientTypes, 53 | TContentItem 54 | >; 55 | 56 | /** 57 | * Gets query for single item 58 | * @param {string} codename - Codename of item to retrieve 59 | */ 60 | item( 61 | codename: string 62 | ): SingleItemQuery; 63 | 64 | /** 65 | * Gets query for multiple taxonomies 66 | */ 67 | taxonomies(): TaxonomiesQuery; 68 | 69 | /** 70 | * Gets query for single item 71 | * @param {string} codename - Codename of taxonomy to retrieve 72 | */ 73 | taxonomy(codename: TClientTypes['taxonomyCodenames']): TaxonomyQuery; 74 | 75 | /** 76 | * Gets query for an element within a type 77 | */ 78 | element( 79 | typeCodename: TClientTypes['contentTypeCodenames'], 80 | elementCodename: TClientTypes['elementCodenames'] 81 | ): ElementQuery; 82 | 83 | /** 84 | * Gets query for initializing sync 85 | */ 86 | initializeSync(): InitializeSyncQuery; 87 | 88 | /** 89 | * Gets query fetching delta updates of content items 90 | */ 91 | syncChanges(): SyncChangesQuery; 92 | 93 | /** 94 | * Item listing of where an asset is used 95 | */ 96 | assetUsedIn(assetCodename: string): UsedInQuery; 97 | 98 | /** 99 | * Item listing of where a content item is used 100 | */ 101 | itemUsedIn(itemCodename: string): UsedInQuery; 102 | } 103 | -------------------------------------------------------------------------------- /lib/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from './delivery-client'; 2 | export * from './idelivery-client.interface'; 3 | export * from './delivery-client.factory'; 4 | -------------------------------------------------------------------------------- /lib/config/delivery-configs.ts: -------------------------------------------------------------------------------- 1 | import { IHeader, IHttpService, IRetryStrategyOptions } from '@kontent-ai/core-sdk'; 2 | 3 | import { ElementResolver } from '../elements'; 4 | import { IProxyUrlData, IQueryConfig, LinkedItemsReferenceHandler } from '../models'; 5 | 6 | export interface IDeliveryClientProxyConfig { 7 | /** 8 | * Base url used for preview reqeusts. Defaults to 'preview-deliver.kontent.ai' 9 | */ 10 | basePreviewUrl?: string; 11 | 12 | /** 13 | * Can be used to generate custom request urls. 14 | * Useful when you have a proxy server and need to transform url to a specific format 15 | * and setting 'baseUrl' is not sufficient. 16 | */ 17 | advancedProxyUrlResolver?: (data: IProxyUrlData) => string; 18 | 19 | /** 20 | * Base url used for all requests. Defaults to 'deliver.kontent.ai' 21 | */ 22 | baseUrl?: string; 23 | } 24 | 25 | export interface IDeliveryClientConfig { 26 | /** 27 | * EnvironmentId 28 | */ 29 | environmentId: string; 30 | 31 | /** 32 | * Preview API key 33 | */ 34 | previewApiKey?: string; 35 | 36 | /** 37 | * Secure API key 38 | * Important: Use secured API only when running on Node.JS server, otherwise 39 | * your key would be exposed in browsers 40 | */ 41 | secureApiKey?: string; 42 | 43 | /** 44 | * Resolver used for using custom models for custom elements. 45 | */ 46 | elementResolver?: ElementResolver; 47 | 48 | /** 49 | * Proxy configuration 50 | */ 51 | proxy?: IDeliveryClientProxyConfig; 52 | 53 | /** 54 | * Default language of content items 55 | */ 56 | defaultLanguage?: string; 57 | 58 | /** 59 | * Retry policy configuration 60 | */ 61 | retryStrategy?: IRetryStrategyOptions; 62 | 63 | /** 64 | * Can be used to inject custom Http service to perform requests 65 | */ 66 | httpService?: IHttpService; 67 | 68 | /** 69 | * Extra headers added to each http request 70 | */ 71 | globalHeaders?: (queryConfig: IQueryConfig) => IHeader[]; 72 | 73 | /** 74 | * Default query configuration. Can be overriden by individual queries. 75 | */ 76 | defaultQueryConfig?: IQueryConfig; 77 | 78 | /** 79 | * Indicates how linked item references are handled (can be used to disable refence mapping when you encounter an issue 80 | * with circular refences) 81 | */ 82 | linkedItemsReferenceHandler?: LinkedItemsReferenceHandler; 83 | 84 | /** 85 | * Sets custom domain for assets 86 | */ 87 | assetsDomain?: string; 88 | 89 | /** 90 | * Codename of rendition preset to be applied by default to the base asset URL path. 91 | */ 92 | defaultRenditionPreset?: string; 93 | 94 | /** 95 | * Can be used to exclude archived items from all queries by default. Only applicable when preview API is used 96 | */ 97 | excludeArchivedItems?: boolean; 98 | } 99 | -------------------------------------------------------------------------------- /lib/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './delivery-configs'; 2 | -------------------------------------------------------------------------------- /lib/contracts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './contracts'; 2 | -------------------------------------------------------------------------------- /lib/elements/element-models.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { IContentItemSystemAttributes } from '../models/item-models'; 3 | import { ElementType } from './element-type'; 4 | 5 | export namespace ElementModels { 6 | export interface IElementWrapper { 7 | element: string; 8 | system: IContentItemSystemAttributes; 9 | rawElement: Contracts.IElementContract; 10 | } 11 | 12 | export interface IElement { 13 | /** 14 | * Name of the element 15 | */ 16 | name: string; 17 | 18 | /** 19 | * Element type 20 | */ 21 | type: ElementType; 22 | 23 | /** 24 | * Element value 25 | */ 26 | value: TValue; 27 | } 28 | 29 | export interface AssetModel { 30 | /** 31 | * Name of the asset 32 | */ 33 | name: string; 34 | 35 | /** 36 | * Type of the asset 37 | */ 38 | type: string; 39 | 40 | /** 41 | * Size of the asset 42 | */ 43 | size: number; 44 | 45 | /** 46 | * Description of the asset 47 | */ 48 | description: string | null; 49 | 50 | /** 51 | * Url of the asset 52 | */ 53 | url: string; 54 | 55 | /** 56 | * Width in pixels for image assets 57 | */ 58 | width: number | null; 59 | 60 | /** 61 | * Height in pixels for image assets 62 | */ 63 | height: number | null; 64 | 65 | /** 66 | * Dictionary with rendition preset codenames as keys and respective renditions as values. 67 | */ 68 | renditions: { [renditionPresetCodename: string]: Rendition } | null; 69 | } 70 | 71 | export interface Rendition { 72 | rendition_id: string; 73 | preset_id: string; 74 | width: number; 75 | height: number; 76 | query: string; 77 | url: string; 78 | } 79 | 80 | export interface MultipleChoiceOption { 81 | name: string; 82 | codename: TOptionCodename; 83 | } 84 | 85 | export interface TaxonomyTerm { 86 | name: string; 87 | codename: TTaxonomyCodename; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/elements/element-resolver.ts: -------------------------------------------------------------------------------- 1 | import { ElementModels } from './element-models'; 2 | 3 | export type ElementResolver = (element: ElementModels.IElementWrapper) => any; 4 | -------------------------------------------------------------------------------- /lib/elements/element-type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents codename of Kontent.ai element types 3 | */ 4 | export enum ElementType { 5 | Text = 'text', 6 | Number = 'number', 7 | ModularContent = 'modular_content', 8 | Asset = 'asset', 9 | DateTime = 'date_time', 10 | RichText = 'rich_text', 11 | MultipleChoice = 'multiple_choice', 12 | UrlSlug = 'url_slug', 13 | Taxonomy = 'taxonomy', 14 | Custom = 'custom', 15 | Unknown = 'unknown' 16 | } 17 | -------------------------------------------------------------------------------- /lib/elements/elements.ts: -------------------------------------------------------------------------------- 1 | import { IContentItem, ILink, IRichTextImage } from '../models'; 2 | import { ElementModels } from './element-models'; 3 | 4 | export namespace Elements { 5 | export type TextElement = ElementModels.IElement; 6 | 7 | export type LinkedItemsElement = ElementModels.IElement< 8 | string[] 9 | > & { 10 | /** 11 | * Linked items 12 | */ 13 | linkedItems: TContentItem[]; 14 | }; 15 | 16 | export type MultipleChoiceElement = ElementModels.IElement< 17 | ElementModels.MultipleChoiceOption[] 18 | >; 19 | 20 | export type DateTimeElement = ElementModels.IElement & { 21 | /** 22 | * Display time zone 23 | */ 24 | displayTimeZone: string | null; 25 | }; 26 | 27 | export type RichTextElement = ElementModels.IElement & { 28 | /** 29 | * Links 30 | */ 31 | links: ILink[]; 32 | 33 | /** 34 | * Images included within rich text element 35 | */ 36 | images: IRichTextImage[]; 37 | 38 | /** 39 | * Array of linked item codenames 40 | */ 41 | linkedItemCodenames: string[]; 42 | 43 | /** 44 | * Array of linked items retrieved from `modular_content` part of the response. Not all items might be here 45 | * as it depends on the `depth` parameter of query. 46 | * The `linkedItemsReferenceHandler` configuration can be used to disable mapping of linked items 47 | */ 48 | linkedItems: TContentItem[]; 49 | }; 50 | 51 | export type NumberElement = ElementModels.IElement; 52 | 53 | export type AssetsElement = ElementModels.IElement; 54 | 55 | export type UrlSlugElement = ElementModels.IElement; 56 | 57 | export type TaxonomyElement< 58 | TaxonomyCodenames extends string = string, 59 | TaxonomyGroupCodename extends string = string 60 | > = ElementModels.IElement[]> & { 61 | /** 62 | * Taxonomy group 63 | */ 64 | taxonomyGroup: TaxonomyGroupCodename; 65 | }; 66 | 67 | export type UnknownElement = ElementModels.IElement; 68 | 69 | export type CustomElement = ElementModels.IElement; 70 | } 71 | -------------------------------------------------------------------------------- /lib/elements/index.ts: -------------------------------------------------------------------------------- 1 | export * from './element-models'; 2 | export * from './element-type'; 3 | export * from './elements'; 4 | export * from './element-resolver'; 5 | -------------------------------------------------------------------------------- /lib/images/image-url-transformation-builder.factory.ts: -------------------------------------------------------------------------------- 1 | import { ImageUrlTransformationBuilder } from './image-url-transformation-builder'; 2 | 3 | export function transformImageUrl(url: string): ImageUrlTransformationBuilder { 4 | return new ImageUrlTransformationBuilder(url); 5 | } 6 | -------------------------------------------------------------------------------- /lib/images/image.models.ts: -------------------------------------------------------------------------------- 1 | export type ImageFitMode = 'clip' | 'scale' | 'crop'; 2 | export type ImageFormat = 'gif' | 'png' |'png8' | 'jpg' | 'pjpg' |'webp'| 'mp4'; 3 | export type ImageCompression = 'lossless' | 'lossy'; 4 | 5 | -------------------------------------------------------------------------------- /lib/images/index.ts: -------------------------------------------------------------------------------- 1 | export * from './image-url-transformation-builder'; 2 | export * from './image.models'; 3 | export * from './image-url-transformation-builder.factory'; 4 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | // Public API 2 | export * from './client'; 3 | export * from './config'; 4 | export * from './contracts'; 5 | export * from './elements'; 6 | export * from './images'; 7 | export * from './mappers'; 8 | export * from './models'; 9 | export * from './query'; 10 | export * from './sdk-info.generated'; 11 | export * from './services'; 12 | export * from './utilities'; 13 | -------------------------------------------------------------------------------- /lib/mappers/generic-element.mapper.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { IGenericElement } from '../models'; 3 | 4 | export class GenericElementMapper { 5 | mapElement(response: Contracts.IViewContentTypeElementContract): IGenericElement { 6 | if (!response) { 7 | throw Error(`Invalid response for mapping element`); 8 | } 9 | 10 | const element = response; 11 | 12 | return { 13 | codename: element.codename, 14 | name: element.name, 15 | type: element.type, 16 | options: element.options ? element.options : [], 17 | taxonomyGroup: element.taxonomy_group 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/mappers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './element.mapper'; 2 | export * from './item.mapper'; 3 | export * from './taxonomy.mapper'; 4 | export * from './type.mapper'; 5 | export * from './generic-element.mapper'; 6 | export * from './language.mapper'; 7 | export * from './used-in.mapper'; 8 | export * from './sync.mapper'; 9 | -------------------------------------------------------------------------------- /lib/mappers/language.mapper.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { ILanguage } from '../models'; 3 | 4 | export class LanguageMapper { 5 | mapMultipleLanguages(response: Contracts.IListLanguagesContract): ILanguage[] { 6 | return response.languages.map((language) => { 7 | return this.mapLanguage(language); 8 | }); 9 | } 10 | 11 | private mapLanguage(language: Contracts.ILanguageContract): ILanguage { 12 | if (!language) { 13 | throw Error(`Cannot map language`); 14 | } 15 | 16 | return { 17 | system: { 18 | codename: language.system.codename as TLanguageCodenames, 19 | id: language.system.id, 20 | name: language.system.name 21 | } 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/mappers/sync.mapper.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { IContentItemDelta } from '../models'; 3 | 4 | export class SyncMapper { 5 | mapContentItemDelta(itemDeltaContract: Contracts.IContentItemDeltaContract): IContentItemDelta { 6 | const systemContract = itemDeltaContract.data.system; 7 | 8 | const elements: Contracts.IContentItemElementsContracts = itemDeltaContract.data.elements 9 | ? itemDeltaContract.data.elements 10 | : {}; 11 | 12 | return { 13 | changeType: itemDeltaContract.change_type, 14 | timestamp: itemDeltaContract.timestamp, 15 | data: { 16 | elements: elements, 17 | system: { 18 | codename: systemContract.codename, 19 | collection: systemContract.collection, 20 | id: systemContract.id, 21 | language: systemContract.language, 22 | lastModified: systemContract.last_modified, 23 | name: systemContract.name, 24 | sitemapLocations: systemContract.sitemap_locations, 25 | type: systemContract.type, 26 | workflowStep: systemContract.workflow_step ?? null, 27 | workflow: systemContract.workflow ?? null 28 | } 29 | } 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/mappers/taxonomy.mapper.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { ITaxonomyGroup, ITaxonomySystemAttributes, ITaxonomyTerms } from '../models'; 3 | 4 | export class TaxonomyMapper { 5 | mapTaxonomy( 6 | taxonomySystem: Contracts.ITaxonomySystemAttributesContract, 7 | taxonomyTerms: Contracts.ITaxonomyTermsContract[] 8 | ): ITaxonomyGroup { 9 | if (!taxonomySystem) { 10 | throw Error(`Cannot map taxonomy due to missing 'system' property`); 11 | } 12 | 13 | if (!taxonomyTerms) { 14 | throw Error(`Cannot map taxonomy due to missing 'terms' property`); 15 | } 16 | 17 | if (!Array.isArray(taxonomyTerms)) { 18 | throw Error(`Cannot map terms because no terms array was provided`); 19 | } 20 | 21 | const mappedSystemAttributes: ITaxonomySystemAttributes = { 22 | name: taxonomySystem.name, 23 | codename: taxonomySystem.codename as TaxonomyCodenames, 24 | id: taxonomySystem.id, 25 | lastModified: taxonomySystem.last_modified 26 | }; 27 | 28 | const mappedTerms: ITaxonomyTerms[] = this.mapTaxonomyTerms(taxonomyTerms); 29 | 30 | return { 31 | system: mappedSystemAttributes, 32 | terms: mappedTerms 33 | }; 34 | } 35 | 36 | mapTaxonomies(taxonomies: Contracts.ITaxonomyGroupContract[]): ITaxonomyGroup[] { 37 | if (!taxonomies) { 38 | throw Error(`Cannot map taxonomy due to missing 'taxonomies' property`); 39 | } 40 | 41 | if (!Array.isArray(taxonomies)) { 42 | throw Error(`Cannot map taxonomies because the 'taxonomies' property is not an array `); 43 | } 44 | 45 | const mappedTaxonomies: ITaxonomyGroup[] = []; 46 | 47 | taxonomies.forEach((taxonomy) => { 48 | mappedTaxonomies.push(this.mapTaxonomy(taxonomy.system, taxonomy.terms)); 49 | }); 50 | 51 | return mappedTaxonomies; 52 | } 53 | 54 | /** 55 | * Recursively map array of taxonomy terms 56 | * @param termsArray Terms array to map 57 | */ 58 | private mapTaxonomyTerms(termsArray: Contracts.ITaxonomyTermsContract[]): ITaxonomyTerms[] { 59 | if (termsArray.length === 0) { 60 | return []; 61 | } 62 | 63 | const mappedTermsArray: ITaxonomyTerms[] = []; 64 | 65 | termsArray.forEach((terms) => { 66 | const mappedTerms: ITaxonomyTerms = { 67 | codename: terms.codename, 68 | name: terms.name, 69 | terms: this.mapTaxonomyTerms(terms.terms) 70 | }; 71 | 72 | mappedTermsArray.push(mappedTerms); 73 | }); 74 | 75 | return mappedTermsArray; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/mappers/type.mapper.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { IContentType, IContentTypeSystemAttributes, IGenericElement } from '../models'; 3 | 4 | export class TypeMapper { 5 | mapSingleType(response: Contracts.IViewContentTypeContract): IContentType { 6 | return this.mapType(response); 7 | } 8 | 9 | mapMultipleTypes(response: Contracts.IListContentTypeContract): IContentType[] { 10 | return response.types.map((type) => { 11 | return this.mapType(type); 12 | }); 13 | } 14 | 15 | private mapType(type: Contracts.IContentTypeContract): IContentType { 16 | if (!type) { 17 | throw Error(`Cannot map type`); 18 | } 19 | 20 | if (!type.elements) { 21 | throw Error(`Cannot map type elements`); 22 | } 23 | 24 | const system: IContentTypeSystemAttributes = { 25 | codename: type.system.codename as TContentTypeCodenames, 26 | id: type.system.id, 27 | name: type.system.name, 28 | lastModified: type.system.last_modified 29 | }; 30 | 31 | const elements: IGenericElement[] = []; 32 | 33 | const elementNames = Object.getOwnPropertyNames(type.elements); 34 | elementNames.forEach((elementName: string) => { 35 | const typeElement = type.elements[elementName]; 36 | 37 | if (!typeElement) { 38 | throw Error(`Cannot find element '${elementName}' on type '${type.system.codename}'`); 39 | } 40 | 41 | // use json property as a codename of the type element 42 | const elementCodename = elementName; 43 | 44 | // extra properties for certain element types 45 | const options: Contracts.IElementOptionContract[] = []; 46 | 47 | // some elements can contain options 48 | const rawOptions = typeElement.options; 49 | if (rawOptions) { 50 | if (!Array.isArray(rawOptions)) { 51 | throw Error(`Content type 'options' property has to be an array`); 52 | } 53 | 54 | rawOptions.forEach((rawOption) => { 55 | options.push({ 56 | codename: rawOption.codename, 57 | name: rawOption.name 58 | }); 59 | }); 60 | } 61 | 62 | elements.push({ 63 | codename: elementCodename, 64 | taxonomyGroup: typeElement.taxonomy_group, 65 | options: options, 66 | name: typeElement.name, 67 | type: typeElement.type 68 | }); 69 | }); 70 | 71 | return { 72 | elements: elements, 73 | system: system 74 | }; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/mappers/used-in.mapper.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { ClientTypes, IUsedInItemRecord } from '../models'; 3 | 4 | export class UsedInMapper { 5 | mapUsedInItem(response: Contracts.IUsedInItemContract): IUsedInItemRecord { 6 | return { 7 | system: { 8 | id: response.system.id, 9 | name: response.system.name, 10 | codename: response.system.codename, 11 | language: response.system.language, 12 | type: response.system.type, 13 | collection: response.system.collection, 14 | workflow: response.system.workflow, 15 | workflowStep: response.system.workflow_step, 16 | lastModified: response.system.last_modified 17 | } 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/models/common/base-responses.ts: -------------------------------------------------------------------------------- 1 | import { IResponse } from '@kontent-ai/core-sdk'; 2 | 3 | export interface IDeliveryNetworkResponse { 4 | data: TData; 5 | response: IResponse; 6 | xContinuationToken?: string; 7 | hasStaleContent: boolean; 8 | } 9 | 10 | export interface IGroupedNetworkResponse { 11 | data: TData; 12 | responses: IDeliveryNetworkResponse[]; 13 | } 14 | -------------------------------------------------------------------------------- /lib/models/common/common-models.ts: -------------------------------------------------------------------------------- 1 | import { IHeader, IHttpCancelRequestToken, IQueryParameter } from '@kontent-ai/core-sdk'; 2 | import { IDeliveryNetworkResponse } from './base-responses'; 3 | import { IPagination } from './pagination.class'; 4 | 5 | export type LinkedItemsReferenceHandler = 'map' | 'ignore'; 6 | 7 | export interface IProxyUrlData { 8 | action: string; 9 | domain: string; 10 | queryParameters: IQueryParameter[]; 11 | queryString: string; 12 | queryConfig: IQueryConfig; 13 | environmentId: string; 14 | } 15 | 16 | export interface ISDKInfo { 17 | /** 18 | * Name of SDK 19 | */ 20 | name: string; 21 | /** 22 | * Version SDK 23 | */ 24 | version: string; 25 | /** 26 | * Host of SDK 27 | */ 28 | host: string; 29 | } 30 | 31 | export interface IKontentResponse {} 32 | 33 | export interface IKontentListWithHeaderResponse extends IKontentResponse { 34 | items: any[]; 35 | } 36 | 37 | export interface IKontentListResponse extends IKontentResponse { 38 | pagination?: IPagination; 39 | items: any[]; 40 | } 41 | 42 | export interface IKontentListAllResponse extends IKontentResponse { 43 | responses: any[]; 44 | items: any[]; 45 | } 46 | 47 | export interface IListAllQueryConfig { 48 | /** 49 | * Number of pages to get. If not set, all available pages are fetched. 50 | */ 51 | pages?: number; 52 | 53 | /** 54 | * Delay between each HTTP requests 55 | */ 56 | delayBetweenRequests?: number; 57 | 58 | /** 59 | * Executed when a list response is loaded 60 | */ 61 | responseFetched?: ( 62 | response: IDeliveryNetworkResponse, 63 | nextPageUrl?: string, 64 | continuationToken?: string 65 | ) => void; 66 | } 67 | 68 | export interface IQueryConfig { 69 | /** 70 | * Indicates if query should use preview mode. Overrides default configuration 71 | */ 72 | usePreviewMode?: boolean; 73 | 74 | /** 75 | * Indicates if query should use secured delivery API mode. Overrides default configuration 76 | */ 77 | useSecuredMode?: boolean; 78 | 79 | /** 80 | * If the requested content has changed since the last request, the header determines whether 81 | * to wait while fetching content. This can be useful when retrieving changed content 82 | * in reaction to a webhook call. By default, when the header is not set, the API 83 | * serves old content (if cached by the CDN) while it's fetching the new content 84 | * to minimize wait time. 85 | */ 86 | waitForLoadingNewContent?: boolean; 87 | 88 | /** 89 | * Extra headers added to request 90 | */ 91 | customHeaders?: IHeader[]; 92 | 93 | /** 94 | * Cancel token 95 | */ 96 | cancelToken?: IHttpCancelRequestToken; 97 | } 98 | 99 | export interface IDeliveryErrorRaw { 100 | message: string; 101 | request_id: string | null; 102 | error_code: number; 103 | specific_code: number; 104 | } 105 | 106 | export class DeliveryError { 107 | public message: string; 108 | public requestId: string | null; 109 | public errorCode: number; 110 | public specificCode: number; 111 | 112 | constructor(data: { message: string; requestId: string | null; errorCode: number; specificCode: number }) { 113 | this.message = data.message; 114 | this.requestId = data.requestId; 115 | this.errorCode = data.errorCode; 116 | this.specificCode = data.specificCode; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/models/common/headers.ts: -------------------------------------------------------------------------------- 1 | export const continuationTokenHeaderName: string = 'X-Continuation'; 2 | export const waitForLoadingNewContentHeader: string = 'X-KC-Wait-For-Loading-New-Content'; 3 | export const sdkVersionHeader: string = 'X-KC-SDKID'; 4 | export const staleContentHeaderName: string = 'X-Stale-Content'; 5 | 6 | -------------------------------------------------------------------------------- /lib/models/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './filters'; 2 | export * from './pagination.class'; 3 | export * from './parameters'; 4 | export * from './sort-order'; 5 | export * from './common-models'; 6 | export * from './base-responses'; 7 | export * from './headers'; 8 | -------------------------------------------------------------------------------- /lib/models/common/pagination.class.ts: -------------------------------------------------------------------------------- 1 | export interface IPagination { 2 | skip: number; 3 | limit: number; 4 | count: number; 5 | nextPage: string; 6 | totalCount: number | null; 7 | } 8 | -------------------------------------------------------------------------------- /lib/models/common/sort-order.ts: -------------------------------------------------------------------------------- 1 | export type SortOrder = 'asc' | 'desc'; 2 | -------------------------------------------------------------------------------- /lib/models/content-type-models.ts: -------------------------------------------------------------------------------- 1 | import { IQueryConfig } from './common/common-models'; 2 | import { IGenericElement } from './element-models'; 3 | 4 | export interface IContentTypeSystemAttributes { 5 | /** 6 | * Id of the type 7 | */ 8 | id: string; 9 | 10 | /** 11 | * Name of the type 12 | */ 13 | name: string; 14 | 15 | /** 16 | * Codename of the type 17 | */ 18 | codename: TContentTypeCodename; 19 | 20 | /** 21 | * Date of last modification 22 | */ 23 | lastModified: Date; 24 | } 25 | 26 | export interface IContentType { 27 | /** 28 | * Content type system attributes 29 | */ 30 | system: IContentTypeSystemAttributes; 31 | 32 | /** 33 | * Elements (elements) assigned to content type 34 | */ 35 | elements: IGenericElement[]; 36 | } 37 | 38 | export interface IContentTypeQueryConfig extends IQueryConfig { 39 | /** 40 | * No dedicated properties required at this moment 41 | */ 42 | } 43 | -------------------------------------------------------------------------------- /lib/models/element-models.ts: -------------------------------------------------------------------------------- 1 | import { IQueryConfig } from './common/common-models'; 2 | 3 | export interface IElementQueryConfig extends IQueryConfig { 4 | /** 5 | * No dedicated properties required at this moment 6 | */ 7 | } 8 | 9 | export interface IGenericElementOption { 10 | /** 11 | * Name of the option 12 | */ 13 | name: string; 14 | 15 | /** 16 | * Value of the option 17 | */ 18 | codename: string; 19 | } 20 | 21 | export interface IGenericElement { 22 | /** 23 | * Indexer 24 | */ 25 | [key: string]: any; 26 | 27 | /** 28 | * Codename of the element 29 | */ 30 | codename?: string; 31 | 32 | /** 33 | * Type of the element 34 | */ 35 | type: string; 36 | 37 | /** 38 | * Name of the element 39 | */ 40 | name: string; 41 | 42 | /** 43 | * Taxonomy group in case the element is a taxonomy 44 | */ 45 | taxonomyGroup?: string; 46 | 47 | /** 48 | * Array of options if the element has some 49 | */ 50 | options: IGenericElementOption[]; 51 | } 52 | -------------------------------------------------------------------------------- /lib/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './element-models'; 3 | export * from './item-models'; 4 | export * from './taxonomy-models'; 5 | export * from './content-type-models'; 6 | export * from './language-models'; 7 | export * from './responses'; 8 | export * from './sync-models'; 9 | 10 | -------------------------------------------------------------------------------- /lib/models/language-models.ts: -------------------------------------------------------------------------------- 1 | import { IQueryConfig } from './common'; 2 | 3 | export interface ILanguageSystem { 4 | id: string; 5 | name: string; 6 | codename: TLanguageCodenames; 7 | } 8 | 9 | export interface ILanguage { 10 | system: ILanguageSystem; 11 | } 12 | 13 | export interface ILanguagesQueryConfig extends IQueryConfig { 14 | /** 15 | * No dedicated properties required at this moment 16 | */ 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/sync-models.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../contracts'; 2 | import { IQueryConfig } from './common/common-models'; 3 | import { IContentItemSystemAttributes } from './item-models'; 4 | 5 | export interface ISyncInitQueryConfig extends IQueryConfig { 6 | /** 7 | * No dedicated properties required at this moment 8 | */ 9 | } 10 | 11 | export interface IContentItemDelta { 12 | data: { 13 | system: IContentItemSystemAttributes; 14 | elements: Contracts.IContentItemElementsContracts; 15 | }; 16 | changeType: 'deleted_item' | 'changed_item'; 17 | timestamp: string; 18 | } 19 | -------------------------------------------------------------------------------- /lib/models/taxonomy-models.ts: -------------------------------------------------------------------------------- 1 | import { IQueryConfig } from './common/common-models'; 2 | 3 | export interface ITaxonomyGroup { 4 | system: ITaxonomySystemAttributes; 5 | terms: ITaxonomyTerms[]; 6 | } 7 | 8 | export interface ITaxonomySystemAttributes { 9 | id: string; 10 | name: string; 11 | codename: TaxonomyCodename; 12 | lastModified: Date; 13 | } 14 | 15 | export interface ITaxonomyTerms { 16 | name: string; 17 | codename: string; 18 | terms: ITaxonomyTerms[]; 19 | } 20 | 21 | export interface ITaxonomyQueryConfig extends IQueryConfig { 22 | /** 23 | * No dedicated properties required at this moment 24 | */ 25 | } 26 | -------------------------------------------------------------------------------- /lib/query/common/base-listing-query.class.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryClientConfig } from '../../config'; 2 | import { 3 | continuationTokenHeaderName, 4 | IGroupedNetworkResponse, 5 | IKontentListAllResponse, 6 | IKontentListResponse, 7 | IDeliveryNetworkResponse, 8 | IListAllQueryConfig, 9 | IQueryConfig, 10 | IContentItem, 11 | ClientTypes, 12 | IUsedInItemRecord, 13 | IContentType, 14 | ITaxonomyGroup, 15 | IContentItemDelta, 16 | ILanguage 17 | } from '../../models'; 18 | import { QueryService } from '../../services'; 19 | import { BaseQuery } from './base-query.class'; 20 | 21 | type ListingRecord = 22 | | IContentItem 23 | | IContentItemDelta 24 | | IUsedInItemRecord 25 | | IContentType 26 | | ILanguage 27 | | ITaxonomyGroup; 28 | 29 | export abstract class BaseListingQuery< 30 | TClientTypes extends ClientTypes, 31 | TRecord extends ListingRecord, 32 | TResponse extends IKontentListResponse, 33 | TAllResponse extends IKontentListAllResponse, 34 | TQueryConfig extends IQueryConfig, 35 | TContract 36 | > extends BaseQuery { 37 | constructor(protected config: IDeliveryClientConfig, protected queryService: QueryService) { 38 | super(config, queryService); 39 | } 40 | 41 | /** 42 | * Sets continuation token header 43 | */ 44 | withContinuationToken(token: string): this { 45 | // remove previous continuation token if there is any 46 | let queryHeaders = this._queryConfig.customHeaders ?? []; 47 | queryHeaders = queryHeaders.filter((m) => m.header !== continuationTokenHeaderName); 48 | 49 | this._queryConfig.customHeaders = queryHeaders; 50 | 51 | this.withHeaders([ 52 | { 53 | header: continuationTokenHeaderName, 54 | value: token 55 | } 56 | ]); 57 | 58 | return this; 59 | } 60 | 61 | /** 62 | * Query to get all items. Uses paging data and may execute multiple HTTP requests depending on number of items 63 | */ 64 | toAllPromise( 65 | queryAllConfig?: IListAllQueryConfig 66 | ): Promise> { 67 | return this.queryService.getListAllResponse({ 68 | page: 1, 69 | listQueryConfig: queryAllConfig, 70 | allResponseFactory: (items, responses) => { 71 | const response = this.allResponseFactory(items, responses); 72 | 73 | return { 74 | data: response, 75 | responses: responses 76 | }; 77 | }, 78 | getResponse: (nextPageUrl, continuationToken) => { 79 | if (nextPageUrl) { 80 | this.withCustomUrl(nextPageUrl); 81 | } 82 | if (continuationToken) { 83 | this.withContinuationToken(continuationToken); 84 | } 85 | 86 | return this.toPromise(); 87 | } 88 | }); 89 | } 90 | 91 | protected abstract allResponseFactory( 92 | items: TRecord[], 93 | responses: IDeliveryNetworkResponse[] 94 | ): TAllResponse; 95 | } 96 | -------------------------------------------------------------------------------- /lib/query/element/element-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { Responses, IElementQueryConfig, IDeliveryNetworkResponse, ClientTypes } from '../../models'; 4 | import { QueryService } from '../../services'; 5 | import { BaseQuery } from '../common/base-query.class'; 6 | 7 | export class ElementQuery extends BaseQuery< 8 | TClientTypes, 9 | Responses.IViewContentTypeElementResponse, 10 | IElementQueryConfig, 11 | Contracts.IViewContentTypeElementContract 12 | > { 13 | protected _queryConfig: IElementQueryConfig = {}; 14 | 15 | constructor( 16 | protected config: IDeliveryClientConfig, 17 | protected queryService: QueryService, 18 | private typeCodename: string, 19 | private elementCodename: string 20 | ) { 21 | super(config, queryService); 22 | 23 | if (!typeCodename) { 24 | throw Error(`Codename of the type has to be provided`); 25 | } 26 | 27 | if (!elementCodename) { 28 | throw Error(`Codename of the element has to be provided`); 29 | } 30 | } 31 | 32 | toPromise(): Promise< 33 | IDeliveryNetworkResponse 34 | > { 35 | return this.queryService.getElementAsync(this.getUrl(), this._queryConfig ?? {}); 36 | } 37 | 38 | getUrl(): string { 39 | return super.resolveUrlInternal(`/types/${this.typeCodename}/elements/${this.elementCodename}`); 40 | } 41 | 42 | map(json: any): Responses.IViewContentTypeElementResponse { 43 | return this.queryService.mappingService.viewContentTypeElementResponse(json); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/query/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common/base-query.class'; 2 | export * from './element/element-query.class'; 3 | export * from './item/multiple-items-query.class'; 4 | export * from './item/single-item-query.class'; 5 | export * from './taxonomy/taxonomies-query.class'; 6 | export * from './taxonomy/taxonomy-query.class'; 7 | export * from './type/multiple-type-query.class'; 8 | export * from './type/single-type-query.class'; 9 | export * from './items-feed/items-feed-query.class'; 10 | export * from './language/languages-query.class'; 11 | export * from './sync/initialize-sync-query.class'; 12 | export * from './sync/sync-changes-query.class'; 13 | export * from './used-in/used-in-query.class'; 14 | -------------------------------------------------------------------------------- /lib/query/language/languages-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { 4 | ClientTypes, 5 | IDeliveryNetworkResponse, 6 | ILanguage, 7 | ILanguagesQueryConfig, 8 | Parameters, 9 | Responses 10 | } from '../../models'; 11 | import { QueryService } from '../../services'; 12 | import { BaseListingQuery } from '../common/base-listing-query.class'; 13 | 14 | export class LanguagesQuery extends BaseListingQuery< 15 | TClientTypes, 16 | ILanguage, 17 | Responses.IListLanguagesResponse, 18 | Responses.IListLanguagesAllResponse, 19 | ILanguagesQueryConfig, 20 | Contracts.IListLanguagesContract 21 | > { 22 | /** 23 | * Endpoint 24 | */ 25 | protected readonly endpoint: string = 'languages'; 26 | 27 | protected _queryConfig: ILanguagesQueryConfig = {}; 28 | 29 | constructor(protected config: IDeliveryClientConfig, protected queryService: QueryService) { 30 | super(config, queryService); 31 | } 32 | 33 | /** 34 | * Limits the number of taxonomies returned by query 35 | * @param limit Number of taxonomies to load 36 | */ 37 | limitParameter(limit: number): this { 38 | this.parameters.push(new Parameters.LimitParameter(limit)); 39 | return this; 40 | } 41 | 42 | /** 43 | * Skips the selected number of taxonomies 44 | * @param skip Number of taxonomies to skip 45 | */ 46 | skipParameter(skip: number): this { 47 | this.parameters.push(new Parameters.SkipParameter(skip)); 48 | return this; 49 | } 50 | 51 | toPromise(): Promise< 52 | IDeliveryNetworkResponse< 53 | Responses.IListLanguagesResponse, 54 | Contracts.IListLanguagesContract 55 | > 56 | > { 57 | return this.queryService.getLanguages(this.getUrl(), this._queryConfig ?? {}); 58 | } 59 | 60 | getUrl(): string { 61 | const action = '/' + this.endpoint; 62 | 63 | return super.resolveUrlInternal(action); 64 | } 65 | 66 | /** 67 | * Used to configure query 68 | * @param queryConfig Query configuration 69 | */ 70 | queryConfig(queryConfig: ILanguagesQueryConfig): this { 71 | this._queryConfig = queryConfig; 72 | return this; 73 | } 74 | 75 | map(json: any): Responses.IListLanguagesResponse { 76 | return this.queryService.mappingService.listLanguagesResponse(json); 77 | } 78 | 79 | protected allResponseFactory( 80 | items: ILanguage[], 81 | responses: IDeliveryNetworkResponse< 82 | Responses.IListLanguagesResponse, 83 | Contracts.IListLanguagesContract 84 | >[] 85 | ): Responses.IListLanguagesAllResponse { 86 | return { 87 | items: items, 88 | responses: responses 89 | }; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/query/sync/initialize-sync-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { 4 | ClientTypes, 5 | Filters, 6 | IDeliveryNetworkResponse, 7 | ILanguagesQueryConfig, 8 | ISyncInitQueryConfig, 9 | Parameters, 10 | Responses 11 | } from '../../models'; 12 | import { QueryService } from '../../services'; 13 | import { BaseQuery } from '../common/base-query.class'; 14 | 15 | export class InitializeSyncQuery extends BaseQuery< 16 | TClientTypes, 17 | Responses.IInitializeSyncResponse, 18 | ISyncInitQueryConfig, 19 | Contracts.IInitializeSyncContract 20 | > { 21 | protected readonly endpoint: string = 'sync/init'; 22 | 23 | protected _queryConfig: ILanguagesQueryConfig = {}; 24 | 25 | constructor(protected config: IDeliveryClientConfig, protected queryService: QueryService) { 26 | super(config, queryService); 27 | } 28 | 29 | /** 30 | * Gets only item of given type 31 | * @param type Codename of type to get 32 | */ 33 | type(type: string): this { 34 | this.parameters.push(new Filters.TypeFilter(type)); 35 | return this; 36 | } 37 | 38 | /** 39 | * Gets only item from given collection 40 | * @param collection Codename of collection to get 41 | */ 42 | collection(collection: string): this { 43 | this.parameters.push(new Filters.CollectionFilter(collection)); 44 | return this; 45 | } 46 | 47 | /** 48 | * Language codename 49 | * @param languageCodename Codename of the language 50 | */ 51 | languageParameter(languageCodename: string): this { 52 | this.parameters.push(new Parameters.LanguageParameter(languageCodename)); 53 | return this; 54 | } 55 | 56 | toPromise(): Promise< 57 | IDeliveryNetworkResponse 58 | > { 59 | return this.queryService.initializeSync(this.getUrl(), this._queryConfig ?? {}); 60 | } 61 | 62 | getUrl(): string { 63 | const action = '/' + this.endpoint; 64 | 65 | return super.resolveUrlInternal(action); 66 | } 67 | 68 | /** 69 | * Used to configure query 70 | * @param queryConfig Query configuration 71 | */ 72 | queryConfig(queryConfig: ISyncInitQueryConfig): this { 73 | this._queryConfig = queryConfig; 74 | return this; 75 | } 76 | 77 | map(json: any): Responses.IInitializeSyncResponse { 78 | return this.queryService.mappingService.initializeContentSync(json); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/query/sync/sync-changes-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { IItemQueryConfig, IDeliveryNetworkResponse, Responses, ClientTypes, IContentItemDelta } from '../../models'; 4 | import { QueryService } from '../../services'; 5 | import { BaseListingQuery } from '../common/base-listing-query.class'; 6 | 7 | export class SyncChangesQuery extends BaseListingQuery< 8 | TClientTypes, 9 | IContentItemDelta, 10 | Responses.ISyncChangesResponse, 11 | Responses.ISyncChangesAllResponse, 12 | IItemQueryConfig, 13 | Contracts.ISyncChangesContract 14 | > { 15 | protected _queryConfig: IItemQueryConfig = {}; 16 | 17 | private readonly action: string = '/sync'; 18 | 19 | constructor(protected config: IDeliveryClientConfig, protected queryService: QueryService) { 20 | super(config, queryService); 21 | } 22 | 23 | toPromise(): Promise> { 24 | return this.queryService.syncChanges(this.getUrl(), this._queryConfig ?? {}); 25 | } 26 | 27 | getUrl(): string { 28 | return super.resolveUrlInternal(this.action); 29 | } 30 | 31 | /** 32 | * Used to configure query 33 | * @param queryConfig Query configuration 34 | */ 35 | queryConfig(queryConfig: IItemQueryConfig): this { 36 | this._queryConfig = queryConfig; 37 | return this; 38 | } 39 | 40 | map(json: any): Responses.ISyncChangesResponse { 41 | return this.queryService.mappingService.syncChanges(json); 42 | } 43 | 44 | protected allResponseFactory( 45 | items: IContentItemDelta[], 46 | responses: IDeliveryNetworkResponse[] 47 | ): Responses.ISyncChangesAllResponse { 48 | return { 49 | items: items, 50 | responses: responses 51 | }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/query/taxonomy/taxonomies-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { 4 | ClientTypes, 5 | IDeliveryNetworkResponse, 6 | ITaxonomyGroup, 7 | ITaxonomyQueryConfig, 8 | Parameters, 9 | Responses 10 | } from '../../models'; 11 | import { QueryService } from '../../services'; 12 | import { BaseListingQuery } from '../common/base-listing-query.class'; 13 | 14 | export class TaxonomiesQuery extends BaseListingQuery< 15 | TClientTypes, 16 | ITaxonomyGroup, 17 | Responses.IListTaxonomiesResponse, 18 | Responses.IListTaxonomiesAllResponse, 19 | ITaxonomyQueryConfig, 20 | Contracts.IListTaxonomyGroupsContract 21 | > { 22 | /** 23 | * Taxonomies endpoint URL action 24 | */ 25 | protected readonly taxonomiesEndpoint: string = 'taxonomies'; 26 | 27 | protected _queryConfig: ITaxonomyQueryConfig = {}; 28 | 29 | constructor(protected config: IDeliveryClientConfig, protected queryService: QueryService) { 30 | super(config, queryService); 31 | } 32 | 33 | /** 34 | * Limits the number of taxonomies returned by query 35 | * @param limit Number of taxonomies to load 36 | */ 37 | limitParameter(limit: number): this { 38 | this.parameters.push(new Parameters.LimitParameter(limit)); 39 | return this; 40 | } 41 | 42 | /** 43 | * Skips the selected number of taxonomies 44 | * @param skip Number of taxonomies to skip 45 | */ 46 | skipParameter(skip: number): this { 47 | this.parameters.push(new Parameters.SkipParameter(skip)); 48 | return this; 49 | } 50 | 51 | toPromise(): Promise< 52 | IDeliveryNetworkResponse< 53 | Responses.IListTaxonomiesResponse, 54 | Contracts.IListTaxonomyGroupsContract 55 | > 56 | > { 57 | return this.queryService.getTaxonomies(this.getUrl(), this._queryConfig ?? {}); 58 | } 59 | 60 | getUrl(): string { 61 | const action = '/' + this.taxonomiesEndpoint; 62 | 63 | return super.resolveUrlInternal(action); 64 | } 65 | 66 | map(json: any): Responses.IListTaxonomiesResponse { 67 | return this.queryService.mappingService.listTaxonomiesResponse(json); 68 | } 69 | 70 | protected allResponseFactory( 71 | items: ITaxonomyGroup[], 72 | responses: IDeliveryNetworkResponse< 73 | Responses.IListTaxonomiesResponse, 74 | Contracts.IListTaxonomyGroupsContract 75 | >[] 76 | ): Responses.IListTaxonomiesAllResponse { 77 | return { 78 | items: items, 79 | responses: responses 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/query/taxonomy/taxonomy-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { ClientTypes, IDeliveryNetworkResponse, ITaxonomyQueryConfig, Responses } from '../../models'; 4 | import { QueryService } from '../../services'; 5 | import { BaseQuery } from '../common/base-query.class'; 6 | 7 | export class TaxonomyQuery extends BaseQuery< 8 | TClientTypes, 9 | Responses.IViewTaxonomyResponse, 10 | ITaxonomyQueryConfig, 11 | Contracts.IViewTaxonomyGroupContract 12 | > { 13 | /** 14 | * Taxonomies endpoint URL action 15 | */ 16 | protected readonly taxonomiesEndpoint: string = 'taxonomies'; 17 | 18 | protected _queryConfig: ITaxonomyQueryConfig = {}; 19 | 20 | constructor( 21 | protected config: IDeliveryClientConfig, 22 | protected queryService: QueryService, 23 | private taxonomyCodename: string 24 | ) { 25 | super(config, queryService); 26 | 27 | if (!taxonomyCodename) { 28 | throw Error(`Cannot create taxonomy query without codename of the taxonomy`); 29 | } 30 | } 31 | 32 | toPromise(): Promise< 33 | IDeliveryNetworkResponse< 34 | Responses.IViewTaxonomyResponse, 35 | Contracts.IViewTaxonomyGroupContract 36 | > 37 | > { 38 | return this.queryService.getTaxonomy(this.getUrl(), this._queryConfig ?? {}); 39 | } 40 | 41 | getUrl(): string { 42 | const action = '/' + this.taxonomiesEndpoint + '/' + this.taxonomyCodename; 43 | 44 | return super.resolveUrlInternal(action); 45 | } 46 | 47 | map(json: any): Responses.IViewTaxonomyResponse { 48 | return this.queryService.mappingService.viewTaxonomyResponse(json); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/query/type/multiple-type-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { 4 | ClientTypes, 5 | IContentType, 6 | IContentTypeQueryConfig, 7 | IDeliveryNetworkResponse, 8 | Parameters, 9 | Responses 10 | } from '../../models'; 11 | import { QueryService } from '../../services'; 12 | import { BaseListingQuery } from '../common/base-listing-query.class'; 13 | 14 | export class MultipleTypeQuery extends BaseListingQuery< 15 | TClientTypes, 16 | IContentType, 17 | Responses.IListContentTypesResponse, 18 | Responses.IListContentTypesAllResponse, 19 | IContentTypeQueryConfig, 20 | Contracts.IListContentTypeContract 21 | > { 22 | protected _queryConfig: IContentTypeQueryConfig = {}; 23 | 24 | constructor(protected config: IDeliveryClientConfig, protected queryService: QueryService) { 25 | super(config, queryService); 26 | } 27 | 28 | /** 29 | * Used to limit the number of elements returned by query. 30 | * @param elementCodenames Array of element codenames to fetch 31 | */ 32 | elementsParameter(elementCodenames: TClientTypes['elementCodenames'][]): this { 33 | this.parameters.push(new Parameters.ElementsParameter(elementCodenames)); 34 | return this; 35 | } 36 | 37 | /** 38 | * Used to exclude elements returned by query. 39 | * @param elementCodenames Array of element codenames to exclude 40 | */ 41 | excludeElementsParameter(elementCodenames: TClientTypes['elementCodenames'][]): this { 42 | this.parameters.push(new Parameters.ExcludeElementsParameter(elementCodenames)); 43 | return this; 44 | } 45 | 46 | /** 47 | * Limits the number of taxonomies returned by query 48 | * @param limit Number of taxonomies to load 49 | */ 50 | limitParameter(limit: number): this { 51 | this.parameters.push(new Parameters.LimitParameter(limit)); 52 | return this; 53 | } 54 | 55 | /** 56 | * Skips the selected number of taxonomies 57 | * @param skip Number of taxonomies to skip 58 | */ 59 | skipParameter(skip: number): this { 60 | this.parameters.push(new Parameters.SkipParameter(skip)); 61 | return this; 62 | } 63 | 64 | toPromise(): Promise< 65 | IDeliveryNetworkResponse< 66 | Responses.IListContentTypesResponse, 67 | Contracts.IListContentTypeContract 68 | > 69 | > { 70 | return this.queryService.getMultipleTypes(this.getUrl(), this._queryConfig ?? {}); 71 | } 72 | 73 | getUrl(): string { 74 | const action = '/types'; 75 | 76 | return super.resolveUrlInternal(action); 77 | } 78 | 79 | map(json: any): Responses.IListContentTypesResponse { 80 | return this.queryService.mappingService.listContentTypesResponse(json); 81 | } 82 | 83 | protected allResponseFactory( 84 | items: IContentType[], 85 | responses: IDeliveryNetworkResponse< 86 | Responses.IListContentTypesResponse, 87 | Contracts.IListContentTypeContract 88 | >[] 89 | ): Responses.IListContentTypesAllResponse { 90 | return { 91 | items: items, 92 | responses: responses 93 | }; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/query/type/single-type-query.class.ts: -------------------------------------------------------------------------------- 1 | import { Contracts } from '../../contracts'; 2 | import { IDeliveryClientConfig } from '../../config'; 3 | import { ClientTypes, IContentTypeQueryConfig, IDeliveryNetworkResponse, Responses } from '../../models'; 4 | import { QueryService } from '../../services'; 5 | import { BaseQuery } from '../common/base-query.class'; 6 | 7 | export class SingleTypeQuery extends BaseQuery< 8 | TClientTypes, 9 | Responses.IViewContentTypeResponse, 10 | IContentTypeQueryConfig, 11 | Contracts.IViewContentTypeContract 12 | > { 13 | protected _queryConfig: IContentTypeQueryConfig = {}; 14 | 15 | constructor( 16 | protected config: IDeliveryClientConfig, 17 | protected queryService: QueryService, 18 | private typeCodename: string 19 | ) { 20 | super(config, queryService); 21 | 22 | if (!typeCodename) { 23 | throw Error(`Cannot create type query without the codename of the type`); 24 | } 25 | } 26 | 27 | toPromise(): Promise< 28 | IDeliveryNetworkResponse< 29 | Responses.IViewContentTypeResponse, 30 | Contracts.IViewContentTypeContract 31 | > 32 | > { 33 | return this.queryService.getSingleType(this.getUrl(), this._queryConfig ?? {}); 34 | } 35 | 36 | getUrl(): string { 37 | const action = '/types/' + this.typeCodename; 38 | 39 | return super.resolveUrlInternal(action); 40 | } 41 | 42 | map(json: any): Responses.IViewContentTypeResponse { 43 | return this.queryService.mappingService.viewContentTypeResponse(json); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/sdk-info.generated.ts: -------------------------------------------------------------------------------- 1 | 2 | import { ISDKInfo } from '@kontent-ai/core-sdk'; 3 | export const sdkInfo: ISDKInfo = { 4 | host: 'npmjs.com', 5 | version: '16.2.0', 6 | name: '@kontent-ai/delivery-sdk' 7 | }; 8 | -------------------------------------------------------------------------------- /lib/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './delivery-query.service'; 2 | export * from './mapping.service'; 3 | -------------------------------------------------------------------------------- /lib/utilities/codename.helper.ts: -------------------------------------------------------------------------------- 1 | export class CodenameHelper { 2 | /** 3 | * This is used to prevent errors caused by reserved names in content item codename (e.g. 'constructor') 4 | */ 5 | escapeCodenameInCodenameIndexer(codename: string): string { 6 | return `_${codename}`; 7 | } 8 | } 9 | 10 | export const codenameHelper = new CodenameHelper(); 11 | -------------------------------------------------------------------------------- /lib/utilities/delivery-url.helper.ts: -------------------------------------------------------------------------------- 1 | export class DeliveryUrlHelper { 2 | replaceAssetDomain(originalAssetUrl: string, customDomain: string): string { 3 | return `${customDomain}${this.getPathname(originalAssetUrl)}`; 4 | } 5 | 6 | getPathname(url: string): string { 7 | return new URL(url).pathname; 8 | } 9 | } 10 | 11 | export const deliveryUrlHelper = new DeliveryUrlHelper(); 12 | -------------------------------------------------------------------------------- /lib/utilities/enum.helper.ts: -------------------------------------------------------------------------------- 1 | export class EnumHelper { 2 | getAllValues(T: any): string[] { 3 | const allEnumValues: string[] = Object.keys(T).map((key) => T[key]); 4 | return allEnumValues; 5 | } 6 | 7 | getEnumFromValue(T: any, value: string | number): T | undefined { 8 | try { 9 | if (!value) { 10 | return undefined; 11 | } 12 | 13 | // we can map back from index number directly 14 | if (this.isNumeric(value)) { 15 | return T[value]; 16 | } 17 | 18 | // for strings, we need to compare each value separately 19 | const allEnumValues = this.getAllValues(T); 20 | 21 | const result = allEnumValues.find((m) => m.toLowerCase() === value.toString().toLowerCase()); 22 | 23 | if (!result) { 24 | return undefined; 25 | } 26 | 27 | return result as T; 28 | } catch (err) { 29 | return undefined; 30 | } 31 | } 32 | 33 | private isNumeric(value: any): boolean { 34 | return !isNaN(parseFloat(value)) && isFinite(value); 35 | } 36 | } 37 | 38 | export const enumHelper = new EnumHelper(); 39 | -------------------------------------------------------------------------------- /lib/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export * from './codename.helper'; 2 | export * from './delivery-url.helper'; 3 | export * from './enum.helper'; 4 | -------------------------------------------------------------------------------- /misc/set-sdk-version.ts: -------------------------------------------------------------------------------- 1 | import { version, name } from '../package.json'; 2 | import { createSdkVersionFile } from './version-helper'; 3 | 4 | createSdkVersionFile('./lib/sdk-info.generated.ts', version, name, '@kontent-ai/core-sdk'); 5 | -------------------------------------------------------------------------------- /misc/verify-sdk-version.ts: -------------------------------------------------------------------------------- 1 | import { sdkInfo } from '../lib/sdk-info.generated'; 2 | import { version } from '../package.json'; 3 | import { verifySdkVersion } from './version-helper'; 4 | 5 | verifySdkVersion(sdkInfo, version); 6 | -------------------------------------------------------------------------------- /misc/version-helper.ts: -------------------------------------------------------------------------------- 1 | import { ISDKInfo } from '@kontent-ai/core-sdk'; 2 | import { cyan, green, red, yellow } from 'colors'; 3 | import { writeFileSync } from 'fs'; 4 | 5 | export const verifySdkVersion = (sdkInfo: ISDKInfo, versionInPackage: string) => { 6 | if (sdkInfo.version !== versionInPackage) { 7 | const msg = `Versions of '${sdkInfo.name}' SDK don't match. Lib version is '${sdkInfo.version}' while package version is '${versionInPackage}'. 8 | Please make sure to use identical versions.`; 9 | throw Error(red(msg)); 10 | } else { 11 | console.log(`Version check successful for '${yellow(sdkInfo.version)}' and package '${yellow(sdkInfo.name)}'`); 12 | } 13 | }; 14 | 15 | export const createSdkVersionFile = (filePath: string, appVersion: string, packageName: string, importFrom: string) => { 16 | console.log(cyan('\nCreating SDK version file')); 17 | 18 | const src = ` 19 | import { ISDKInfo } from '${importFrom}'; 20 | export const sdkInfo: ISDKInfo = { 21 | host: 'npmjs.com', 22 | version: '${appVersion}', 23 | name: '${packageName}' 24 | }; 25 | `; 26 | 27 | // ensure version module pulls value from package.json 28 | writeFileSync(filePath, src); 29 | console.log(green(`Updating application version ${yellow(appVersion)}`)); 30 | console.log(`${green('Writing version module to ')}${yellow(filePath)}\n`); 31 | }; 32 | -------------------------------------------------------------------------------- /test/browser/client/delivery-client.spec.ts: -------------------------------------------------------------------------------- 1 | import { DeliveryClient } from '../../../lib'; 2 | 3 | describe('Delivery Client initialization', () => { 4 | it(`initialization DeliveryClient without config should throw error`, () => { 5 | expect(() => new DeliveryClient(null as any)).toThrowError(); 6 | expect(() => new DeliveryClient(undefined as any)).toThrowError(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/base-url.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Base URL', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | let itemsUrl: string; 9 | let parsedUrl: URL; 10 | 11 | beforeAll(() => { 12 | itemsUrl = context.deliveryClient.items().getUrl(); 13 | 14 | parsedUrl = new URL(itemsUrl); 15 | }); 16 | 17 | it(`url should be defined`, () => expect(itemsUrl).toBeDefined()); 18 | 19 | it(`protocol should be 'https:'`, () => expect(parsedUrl.protocol).toEqual('https:')); 20 | 21 | it(`host should be 'deliver.kontent.ai'`, () => expect(parsedUrl.host).toEqual('deliver.kontent.ai')); 22 | 23 | it(`origin should be 'https://deliver.kontent.ai'`, () => expect(parsedUrl.origin).toEqual('https://deliver.kontent.ai')); 24 | 25 | it(`pathname should contain environment id'`, () => expect(itemsUrl).toContain(context.environmentId)); 26 | 27 | it(`custom base URL should be used'`, () => { 28 | const baseUrl = 'http://custombase.com'; 29 | const contextCustom = new Context({baseUrl: baseUrl}); 30 | setup(contextCustom); 31 | expect(contextCustom.deliveryClient.items().getUrl()).toContain(baseUrl); 32 | }); 33 | 34 | it(`custom preview URL should be used'`, () => { 35 | const previewUrl = 'http://custompreview.com'; 36 | const contextCustom = new Context({basePreviewUrl: previewUrl, defaultQueryConfig: { usePreviewMode: true}}); 37 | setup(contextCustom); 38 | expect(contextCustom.deliveryClient.items().getUrl()).toContain(previewUrl); 39 | }); 40 | 41 | }); 42 | 43 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/core-headers.spec.ts: -------------------------------------------------------------------------------- 1 | import { sdkInfo } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | import { IHeader } from '@kontent-ai/core-sdk'; 4 | 5 | describe('Core headers', () => { 6 | 7 | const context = new Context(); 8 | 9 | setup(context); 10 | 11 | it(`SDK Id version header should be set`, () => { 12 | const headers = context.deliveryClient.items().getHeaders(); 13 | const header = headers.find(m => m.header === 'X-KC-SDKID'); 14 | expect(header).toBeDefined(); 15 | }); 16 | 17 | it(`Verifies SDK Id version format`, () => { 18 | const headers = context.deliveryClient.items().getHeaders(); 19 | const header = headers.find(m => m.header === 'X-KC-SDKID') as IHeader; 20 | const expectedValue = `${sdkInfo.host};${sdkInfo.name};${sdkInfo.version}`; 21 | expect(header.value).toEqual(expectedValue); 22 | }); 23 | 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/global-headers.spec.ts: -------------------------------------------------------------------------------- 1 | import { IHeader } from '@kontent-ai/core-sdk'; 2 | 3 | import { IQueryConfig } from '../../../../lib'; 4 | import { Context, setup } from '../../setup'; 5 | 6 | describe('Global headers', () => { 7 | let headers: IHeader[] = []; 8 | const context = new Context(); 9 | let queryConfig: IQueryConfig | undefined; 10 | context.globalHeaders = (xQueryConfig) => { 11 | queryConfig = xQueryConfig; 12 | return [ 13 | { 14 | header: 'gl1', 15 | value: 'vl1', 16 | }, 17 | { 18 | header: 'gl2', 19 | value: 'vl2', 20 | } 21 | ]; 22 | }; 23 | 24 | setup(context); 25 | 26 | beforeAll((done) => { 27 | headers = context.deliveryClient.items().queryConfig({ 28 | usePreviewMode: true 29 | }).getHeaders(); 30 | 31 | done(); 32 | }); 33 | 34 | it(`Global headers should be set`, () => { 35 | const header1 = headers.find(m => m.header === 'gl1') as IHeader; 36 | const header2 = headers.find(m => m.header === 'gl2') as IHeader; 37 | expect(header1).toBeDefined(); 38 | expect(header2).toBeDefined(); 39 | 40 | expect(header1.value).toEqual('vl1'); 41 | expect(header2.value).toEqual('vl2'); 42 | }); 43 | 44 | 45 | it(`Query config should be set and preview mode enabled`, () => { 46 | expect(queryConfig).toBeDefined(); 47 | 48 | if (queryConfig) { 49 | expect(queryConfig.usePreviewMode).toBeTruthy(); 50 | 51 | } 52 | }); 53 | 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/global-query-headers.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Global query headers', () => { 4 | 5 | it(`globally defined header should stay in headers when query config defines empty header array`, () => { 6 | const context = new Context({ 7 | globalHeaders: (config) => [ 8 | { 9 | header: 'xHeader', 10 | value: 'x' 11 | } 12 | ], 13 | defaultQueryConfig: { 14 | customHeaders: [] 15 | } 16 | }); 17 | 18 | setup(context); 19 | const headers = context.deliveryClient.items().getHeaders(); 20 | const xHeader = headers.find(m => m.header === 'xHeader'); 21 | expect(xHeader).toBeDefined(); 22 | }); 23 | 24 | it(`globally defined header should stay in headers along with headers defined by query config`, () => { 25 | const context = new Context({ 26 | globalHeaders: (config) => [ 27 | { 28 | header: 'xHeader', 29 | value: 'x' 30 | } 31 | ], 32 | defaultQueryConfig: { 33 | customHeaders: [{ 34 | header: 'yHeader', 35 | value: 'y' 36 | }] 37 | } 38 | }); 39 | 40 | setup(context); 41 | const headers = context.deliveryClient.items().getHeaders(); 42 | const xHeader = headers.find(m => m.header === 'xHeader'); 43 | const yHeader = headers.find(m => m.header === 'yHeader'); 44 | expect(xHeader).toBeDefined(); 45 | expect(yHeader).toBeDefined(); 46 | }); 47 | 48 | }); 49 | 50 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/preview-headers.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | import { IHeader } from '@kontent-ai/core-sdk'; 3 | 4 | describe('Preview headers', () => { 5 | 6 | const context = new Context({ 7 | defaultQueryConfig: { 8 | usePreviewMode: true 9 | } 10 | }); 11 | 12 | setup(context); 13 | 14 | it(`preview authorization header should be defined when getting items (global config)'`, () => { 15 | const headers = context.deliveryClient.items().getHeaders(); 16 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 17 | expect(authorizationHeader).toBeDefined(); 18 | }); 19 | 20 | it(`preview authorization header should be defined when getting taxonomies (global config)'`, () => { 21 | const headers = context.deliveryClient.types().getHeaders(); 22 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 23 | expect(authorizationHeader).toBeDefined(); 24 | }); 25 | 26 | it(`preview authorization header should be defined for getting types (global config)'`, () => { 27 | const headers = context.deliveryClient.taxonomies().getHeaders(); 28 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 29 | expect(authorizationHeader).toBeDefined(); 30 | }); 31 | 32 | it(`preview authorization header should contain preview API key (global config)'`, () => { 33 | const headers = context.deliveryClient.items().getHeaders(); 34 | const authorizationHeader = headers.find(m => m.header === 'authorization') as IHeader; 35 | const expectedValue = 'bearer ' + context.previewApiKey; 36 | expect(authorizationHeader.value).toEqual(expectedValue); 37 | }); 38 | 39 | it(`preview authorization header should NOT be defined when QueryConfig specifically disables it'`, () => { 40 | const headers = context.deliveryClient.items().queryConfig({ 41 | usePreviewMode: false 42 | }).getHeaders(); 43 | 44 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 45 | 46 | expect(authorizationHeader).toBeUndefined(); 47 | }); 48 | }); 49 | 50 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/preview-url.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Preview URL', () => { 4 | 5 | const context = new Context({ 6 | defaultQueryConfig: { 7 | usePreviewMode: true 8 | } 9 | }); 10 | 11 | setup(context); 12 | 13 | it(`origin should be 'https://preview-deliver.kontent.ai'`, () => { 14 | const url = new URL(context.deliveryClient.items().getUrl()); 15 | expect(url.origin).toEqual('https://preview-deliver.kontent.ai'); 16 | }); 17 | 18 | it(`preview pathname should contain environment id'`, () => { 19 | const url = context.deliveryClient.items().getUrl(); 20 | expect(url).toContain(context.environmentId); 21 | }); 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/preview-with-secured-mode.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Using secured with preview mode', () => { 4 | 5 | const context = new Context({ 6 | defaultQueryConfig: { 7 | usePreviewMode: true, 8 | useSecuredMode: true 9 | } 10 | }); 11 | 12 | setup(context); 13 | 14 | it(`using secured API in combination with preview mode should thrown an Exception`, () => { 15 | expect(() => context.deliveryClient.items().getHeaders()).toThrowError(); 16 | }); 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/proxy-url-spec.ts: -------------------------------------------------------------------------------- 1 | import { DeliveryClient, IProxyUrlData } from '../../../../lib'; 2 | 3 | describe('Proxy URL #1', () => { 4 | 5 | let proxyData: IProxyUrlData | undefined; 6 | 7 | const client = new DeliveryClient({ 8 | environmentId: 'xxx', 9 | proxy: { 10 | advancedProxyUrlResolver: (data) => { 11 | proxyData = data; 12 | return 'http://custom-proxy.io'; 13 | } 14 | } 15 | }); 16 | 17 | const itemsUrl: string = client 18 | .item('xCodename') 19 | .depthParameter(1) 20 | .elementsParameter(['xElement']) 21 | .queryConfig({ 22 | useSecuredMode: true 23 | }) 24 | .getUrl(); 25 | 26 | it(`Custom proxy should be applied as request URL`, () => expect(itemsUrl).toEqual('http://custom-proxy.io')); 27 | 28 | it(`Proxy data should be assigned correctly`, () => { 29 | if (!proxyData) { 30 | throw Error(`Proxy data is invalid`); 31 | } 32 | expect(proxyData.action).toEqual('/items/xCodename'); 33 | expect(proxyData.queryParameters.length).toEqual(2); 34 | expect(proxyData.environmentId).toEqual('xxx'); 35 | expect(proxyData.queryString).toEqual('?depth=1&elements=xElement'); 36 | expect(proxyData.queryConfig.useSecuredMode).toBeTruthy(); 37 | expect(proxyData.domain).toEqual('https://deliver.kontent.ai'); 38 | }); 39 | }); 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/secured-api-headers.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | import { IHeader } from '@kontent-ai/core-sdk'; 3 | 4 | describe('Secured headers', () => { 5 | 6 | const context = new Context({ 7 | defaultQueryConfig: { 8 | useSecuredMode: true 9 | } 10 | }); 11 | 12 | setup(context); 13 | 14 | it(`secured authorization header should be defined when getting items (global config)'`, () => { 15 | const headers = context.deliveryClient.items().getHeaders(); 16 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 17 | expect(authorizationHeader).toBeDefined(); 18 | }); 19 | 20 | it(`secured authorization header should be defined when getting taxonomies (global config)'`, () => { 21 | const headers = context.deliveryClient.types().getHeaders(); 22 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 23 | expect(authorizationHeader).toBeDefined(); 24 | }); 25 | 26 | it(`secured authorization header should be defined for getting types (global config)'`, () => { 27 | const headers = context.deliveryClient.taxonomies().getHeaders(); 28 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 29 | expect(authorizationHeader).toBeDefined(); 30 | }); 31 | 32 | it(`secured authorization header should contain secured API key (global config)'`, () => { 33 | const headers = context.deliveryClient.items().getHeaders(); 34 | const authorizationHeader = headers.find(m => m.header === 'authorization') as IHeader; 35 | const expectedValue = 'bearer ' + context.securedApiKey; 36 | expect(authorizationHeader.value).toEqual(expectedValue); 37 | }); 38 | 39 | it(`secured authorization header should NOT be defined when QueryConfig specifically disables it'`, () => { 40 | const headers = context.deliveryClient.items().queryConfig({ 41 | useSecuredMode: false 42 | }).getHeaders(); 43 | 44 | const authorizationHeader = headers.find(m => m.header === 'authorization'); 45 | 46 | expect(authorizationHeader).toBeUndefined(); 47 | }); 48 | }); 49 | 50 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/core/wait-for-loading-new-content-header.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Wait for loading new content header', () => { 4 | 5 | it(`defaultQueryConfig header should be present in headers`, () => { 6 | const context = new Context({ 7 | defaultQueryConfig: { 8 | waitForLoadingNewContent: true 9 | } 10 | }); 11 | 12 | setup(context); 13 | const headers = context.deliveryClient.items().getHeaders(); 14 | const waitForLoadingNewContentHeader = headers.find(m => m.header === 'X-KC-Wait-For-Loading-New-Content'); 15 | expect(waitForLoadingNewContentHeader).toBeDefined(); 16 | }); 17 | 18 | it(`defaultQueryConfig header should not be present in headers`, () => { 19 | const context = new Context({ 20 | defaultQueryConfig: { 21 | waitForLoadingNewContent: false 22 | } 23 | }); 24 | 25 | setup(context); 26 | const headers = context.deliveryClient.items().getHeaders(); 27 | const waitForLoadingNewContentHeader = headers.find(m => m.header === 'X-KC-Wait-For-Loading-New-Content'); 28 | expect(waitForLoadingNewContentHeader).toBeUndefined(); 29 | }); 30 | 31 | it(`defaultQueryConfig header should be present in headers`, () => { 32 | const context = new Context({ 33 | defaultQueryConfig: { 34 | waitForLoadingNewContent: false 35 | } 36 | }); 37 | 38 | setup(context); 39 | const headers = context.deliveryClient.items().queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 40 | const waitForLoadingNewContentHeader = headers.find(m => m.header === 'X-KC-Wait-For-Loading-New-Content'); 41 | expect(waitForLoadingNewContentHeader).toBeDefined(); 42 | }); 43 | 44 | it(`defaultQueryConfig header should not be present in headers`, () => { 45 | const context = new Context({ 46 | defaultQueryConfig: { 47 | waitForLoadingNewContent: true 48 | } 49 | }); 50 | 51 | setup(context); 52 | const headers = context.deliveryClient.items().queryConfig({ waitForLoadingNewContent: false }).getHeaders(); 53 | const waitForLoadingNewContentHeader = headers.find(m => m.header === 'X-KC-Wait-For-Loading-New-Content'); 54 | expect(waitForLoadingNewContentHeader).toBeUndefined(); 55 | }); 56 | 57 | }); 58 | 59 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/assets/asset-rendition.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "item": { 3 | "system": { 4 | "id": "335d17ac-b6ba-4c6a-ae31-23c1193215cb", 5 | "collection": "default", 6 | "name": "My article", 7 | "codename": "my_article", 8 | "language": "en-US", 9 | "type": "article", 10 | "sitemap_locations": [], 11 | "last_modified": "2019-03-27T13:21:11.38Z", 12 | "workflow_step": "published" 13 | }, 14 | "elements": { 15 | "assetWithRenditionInDefaultPreset": { 16 | "type": "asset", 17 | "name": "Teaser image", 18 | "value": [ 19 | { 20 | "name": "sources.jpg", 21 | "type": "image/jpeg", 22 | "size": 45376, 23 | "description": "Description of what the asset represents.", 24 | "url": "https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/3e76909f-599f-4742-b472-77fd4b510e92/sources.jpg", 25 | "width": 640, 26 | "height": 457, 27 | "renditions": { 28 | "default": { 29 | "rendition_id": "b447ca6c-8020-4e8f-be57-1d110721e535", 30 | "preset_id": "a6d98cd5-8b2c-4e50-99c9-15192bce2490", 31 | "width": 1280, 32 | "height": 1024, 33 | "query": "w=1280&h=1024&fit=clip&rect=2396,169,1280,1024" 34 | } 35 | } 36 | } 37 | ] 38 | }, 39 | "assetWithRenditionInMobilePreset": { 40 | "type": "asset", 41 | "name": "Teaser image", 42 | "value": [ 43 | { 44 | "name": "sources.jpg", 45 | "type": "image/jpeg", 46 | "size": 45376, 47 | "description": "Description of what the asset represents.", 48 | "url": "https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/3e76909f-599f-4742-b472-77fd4b510e92/sources.jpg", 49 | "width": 640, 50 | "height": 457, 51 | "renditions": { 52 | "mobile": { 53 | "rendition_id": "b447ca6c-8020-4e8f-be57-1d110721e535", 54 | "preset_id": "a6d98cd5-8b2c-4e50-99c9-15192bce2490", 55 | "width": 1280, 56 | "height": 1024, 57 | "query": "w=1280&h=1024&fit=clip&rect=2396,169,1280,1024" 58 | } 59 | } 60 | } 61 | ] 62 | }, 63 | "assetWithoutRendition": { 64 | "type": "asset", 65 | "name": "Teaser image", 66 | "value": [ 67 | { 68 | "name": "sources.jpg", 69 | "type": "image/jpeg", 70 | "size": 45376, 71 | "description": "Description of what the asset represents.", 72 | "url": "https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/3e76909f-599f-4742-b472-77fd4b510e92/sources.jpg", 73 | "width": 640, 74 | "height": 457 75 | } 76 | ] 77 | } 78 | } 79 | }, 80 | "modular_content": {} 81 | } 82 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/assets/custom-assets-domain.spec.ts: -------------------------------------------------------------------------------- 1 | import { deliveryUrlHelper } from '../../../../../lib'; 2 | import { getDeliveryClientWithJson, Movie } from '../../../setup'; 3 | import * as responseJson from '../../fake-data/fake-warrior-response.json'; 4 | 5 | describe('Custom assets domain', () => { 6 | let item: Movie; 7 | const customDomain: string = 'https://custom.com'; 8 | 9 | beforeAll(async () => { 10 | const response = await getDeliveryClientWithJson(responseJson, { 11 | assetsDomain: customDomain, 12 | environmentId: 'x' 13 | }) 14 | .item('xx') 15 | .toPromise(); 16 | 17 | item = response.data.item; 18 | }); 19 | 20 | it(`Custom asset domain should be set in asset element`, () => { 21 | const assetElement = item.elements.poster.value[0]; 22 | 23 | expect(assetElement.url).toEqual( 24 | `${customDomain}/da5abe9f-fdad-4168-97cd-b3464be2ccb9/22504ba8-2075-48fa-9d4f-8fce3de1754a/warrior.jpg` 25 | ); 26 | }); 27 | 28 | it(`Custom asset domain should be set for rendition URL in asset element`, () => { 29 | const assetElement = item.elements.poster.value[0]; 30 | 31 | expect(assetElement.renditions?.['default'].url).toEqual( 32 | `${customDomain}/da5abe9f-fdad-4168-97cd-b3464be2ccb9/22504ba8-2075-48fa-9d4f-8fce3de1754a/warrior.jpg?w=1280&h=1024&fit=clip&rect=2396,169,1280,1024` 33 | ); 34 | }); 35 | 36 | it(`Custom asset domain should be set in Rich Text element`, () => { 37 | const richTextElement = item.elements.plot; 38 | 39 | for (const image of richTextElement.images) { 40 | const imagePathname = deliveryUrlHelper.getPathname(image.url); 41 | expect(image.url).toEqual(`${customDomain}${imagePathname}`); 42 | } 43 | }); 44 | 45 | it(`Custom asset domain should be set in HTML of Rich Text elements`, () => { 46 | const richTextElement = item.elements.plot; 47 | 48 | for (const image of richTextElement.images) { 49 | expect(richTextElement.value).toContain(image.url); 50 | } 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/custom-element/custom-element-with-custom-model.spec.ts: -------------------------------------------------------------------------------- 1 | import { IContentItem, ElementType, Elements } from '../../../../../lib'; 2 | import { getDeliveryClientWithJson } from '../../../setup'; 3 | import * as responseJson from './custom-element.spec.json'; 4 | 5 | 6 | type ColorElement = Elements.CustomElement<{ 7 | red: number; 8 | green: number; 9 | blue: number; 10 | }>; 11 | 12 | type MarkdownElement = Elements.CustomElement<{ 13 | isMarkdown: boolean; 14 | 15 | }>; 16 | 17 | type ItemWithCustomElements = IContentItem<{ 18 | color: ColorElement; 19 | markdown: MarkdownElement; 20 | }>; 21 | 22 | describe('Custom element with custom model', () => { 23 | let item: ItemWithCustomElements; 24 | 25 | beforeAll(async () => { 26 | const response = await getDeliveryClientWithJson(responseJson, { 27 | environmentId: '', 28 | elementResolver: (elementWrapper) => { 29 | const responseItem = responseJson.items[0]; 30 | const colorElement = responseJson.items[0].elements.color; 31 | const markdownElement = responseJson.items[0].elements.markdown; 32 | 33 | if ( 34 | elementWrapper.system.type === responseItem.system.type && 35 | elementWrapper.rawElement.name === colorElement.name 36 | ) { 37 | const parsed = JSON.parse(elementWrapper.rawElement.value); 38 | 39 | return { 40 | red: parsed.red, 41 | green: parsed.green, 42 | blue: parsed.blue 43 | }; 44 | } 45 | 46 | if ( 47 | elementWrapper.system.type === responseItem.system.type && 48 | elementWrapper.rawElement.name === markdownElement.name 49 | ) { 50 | return { 51 | isMarkdown: true 52 | }; 53 | } 54 | return undefined; 55 | } 56 | }) 57 | .items() 58 | .toPromise(); 59 | 60 | item = response.data.items[0]; 61 | }); 62 | 63 | it(`Color element should be mapped to ColorElement`, () => { 64 | const element = item.elements.color; 65 | const rawElement = responseJson.items[0].elements.color; 66 | 67 | expect(element.value.red).toEqual(167); 68 | expect(element.value.green).toEqual(96); 69 | expect(element.value.blue).toEqual(197); 70 | 71 | expect(element.name).toEqual(rawElement.name); 72 | expect(element.type).toEqual(ElementType.Custom); 73 | }); 74 | 75 | it(`Markdown element should be mapped to MarkdownElement`, () => { 76 | const element = item.elements.markdown; 77 | const rawElement = responseJson.items[0].elements.markdown; 78 | 79 | expect(element.value.isMarkdown).toEqual(true); 80 | expect(element.name).toEqual(rawElement.name); 81 | expect(element.type).toEqual(ElementType.Custom); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/custom-element/custom-element.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "a3dde145-2456-445d-ac33-3d3f6a092da8", 6 | "name": "Color item", 7 | "codename": "color_item", 8 | "language": "default", 9 | "type": "with_color_picker", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-01-04T09:56:04.372261Z" 13 | }, 14 | "elements": { 15 | "rich": { 16 | "type": "rich_text", 17 | "name": "Rich", 18 | "images": {}, 19 | "links": {}, 20 | "modular_content": [], 21 | "value": "


" 22 | }, 23 | "color": { 24 | "type": "custom", 25 | "name": "Color", 26 | "value": "{\"red\":167,\"green\":96,\"blue\":197}" 27 | }, 28 | "markdown": { 29 | "type": "custom", 30 | "name": "markdown", 31 | "value": "**Super value**" 32 | }, 33 | "simple_text": { 34 | "type": "text", 35 | "name": "Simple text", 36 | "value": "simple text" 37 | } 38 | } 39 | } 40 | ], 41 | "modular_content": {}, 42 | "pagination": { 43 | "skip": 0, 44 | "limit": 0, 45 | "count": 1, 46 | "next_page": "" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/custom-element/custom-element.spec.ts: -------------------------------------------------------------------------------- 1 | import { IContentItem, Elements, ElementType } from '../../../../../lib'; 2 | import { getDeliveryClientWithJson } from '../../../setup'; 3 | import * as responseJson from './custom-element.spec.json'; 4 | 5 | describe('Custom element', () => { 6 | let item: IContentItem; 7 | beforeAll(async () => { 8 | const response = (await getDeliveryClientWithJson(responseJson).items().toPromise()).data; 9 | 10 | item = response.items[0]; 11 | }); 12 | 13 | it(`Color element should be mapped to DefaultCustomElement`, () => { 14 | const element = item.elements.color as Elements.CustomElement; 15 | const rawElement = responseJson.items[0].elements.color; 16 | 17 | expect(element.name).toEqual(rawElement.name); 18 | expect(element.type).toEqual(ElementType.Custom); 19 | expect(element.value).toEqual(rawElement.value); 20 | }); 21 | 22 | it(`Markdown element should be mapped to DefaultCustomElement`, () => { 23 | const element = item.elements.markdown as Elements.CustomElement; 24 | const rawElement = responseJson.items[0].elements.markdown; 25 | 26 | expect(element.name).toEqual(rawElement.name); 27 | expect(element.type).toEqual(ElementType.Custom); 28 | expect(element.value).toEqual(rawElement.value); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/rich-text/rich-text.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses, Elements, ElementType, IContentItem } from '../../../../../lib'; 2 | import { getDeliveryClientWithJson } from '../../../setup'; 3 | import * as responseJson from './rich-text.spec.json'; 4 | 5 | describe('Rich-text element', () => { 6 | let response: Responses.IListContentItemsResponse; 7 | beforeAll(async () => { 8 | response = (await getDeliveryClientWithJson(responseJson).items().toPromise()).data; 9 | }); 10 | 11 | it(`Rich-text linked items should be re-ordered if not ordered in raw response`, async () => { 12 | const rawItem = responseJson.items.find(({ system: { codename } }) => codename === 'rich_text_item_unordered'); 13 | const item = response.items.find( 14 | ({ system: { codename } }) => codename === 'rich_text_item_unordered' 15 | ) as IContentItem; 16 | const element = item.elements.rich as Elements.RichTextElement; 17 | 18 | expect(element.type).toEqual(ElementType.RichText); 19 | expect(rawItem?.elements.rich.modular_content).toEqual(['n2', 'n4', 'n3', 'n1']); 20 | expect(element.linkedItemCodenames).toEqual(['n1', 'n2', 'n3', 'n4']); 21 | for (let i = 0; i < element.linkedItems.length; ++i) { 22 | expect(element.linkedItems[i].system.codename).toBe(`n${i + 1}`); 23 | } 24 | }); 25 | 26 | it(`Rich-text linked items order should be maintained if already ordered in raw response`, () => { 27 | const rawItem = responseJson.items.find(({ system: { codename } }) => codename === 'rich_text_item_ordered'); 28 | const item = response.items.find( 29 | ({ system: { codename } }) => codename === 'rich_text_item_ordered' 30 | ) as IContentItem; 31 | const element = item.elements.rich as Elements.RichTextElement; 32 | 33 | expect(element.type).toEqual(ElementType.RichText); 34 | expect(rawItem?.elements.rich.modular_content).toEqual(['n1', 'n2', 'n3', 'n4']); 35 | expect(element.linkedItemCodenames).toEqual(['n1', 'n2', 'n3', 'n4']); 36 | for (let i = 0; i < element.linkedItems.length; ++i) { 37 | expect(element.linkedItems[i].system.codename).toBe(`n${i + 1}`); 38 | } 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/unknown-element/unknown-element.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "a3dde145-2456-445d-ac33-3d3f6a092da8", 6 | "name": "Color item", 7 | "codename": "color_item", 8 | "language": "default", 9 | "type": "with_color_picker", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-01-04T09:56:04.372261Z" 13 | }, 14 | "elements": { 15 | "ufo": { 16 | "type": "ufo", 17 | "name": "Simple text", 18 | "value": "simple text" 19 | } 20 | } 21 | } 22 | ], 23 | "modular_content": {}, 24 | "pagination": { 25 | "skip": 0, 26 | "limit": 0, 27 | "count": 1, 28 | "next_page": "" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/elements/unknown-element/unknown-element.spec.ts: -------------------------------------------------------------------------------- 1 | import { Elements, ElementType, IContentItem } from '../../../../../lib'; 2 | import { getDeliveryClientWithJson } from '../../../setup'; 3 | import * as responseJson from './unknown-element.spec.json'; 4 | 5 | describe('Unknown element', () => { 6 | let item: IContentItem; 7 | 8 | beforeAll(async () => { 9 | const response = (await getDeliveryClientWithJson(responseJson).items().toPromise()).data; 10 | 11 | item = response.items[0]; 12 | }); 13 | 14 | it(`Ufo element should be mapped to UnknownElement`, () => { 15 | const element = item.elements.ufo as Elements.UnknownElement; 16 | const rawElement = responseJson.items[0].elements.ufo; 17 | 18 | expect(element.name).toEqual(rawElement.name); 19 | expect(element.type).toEqual(ElementType.Unknown); 20 | expect(element.value).toEqual(rawElement.value); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items-feed/items-feed-all.spec.ts: -------------------------------------------------------------------------------- 1 | import { IContentItem, IGroupedNetworkResponse, Responses } from '../../../../../lib'; 2 | import { Context, Movie, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJsonAndHeaders } from '../../../setup'; 4 | import * as responseJson from './items-feed-all.spec.json'; 5 | 6 | describe('Items feed all', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let response: IGroupedNetworkResponse>; 11 | 12 | beforeAll(async () => { 13 | response = await getDeliveryClientWithJsonAndHeaders( 14 | responseJson, 15 | { 16 | environmentId: 'x' 17 | }, 18 | [] 19 | ) 20 | .itemsFeed() 21 | .queryConfig({ 22 | disableItemLinking: false 23 | }) 24 | .toAllPromise(); 25 | }); 26 | 27 | it(`Validate responses counts`, () => { 28 | expect(response.responses.length).toEqual(1); 29 | }); 30 | 31 | it(`Validate items count`, () => { 32 | expect(response.data.items.length).toEqual(responseJson.items.length); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items-feed/items-feed.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse, Responses } from '../../../../../lib'; 2 | import { Context, Movie, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJsonAndHeaders } from '../../../setup'; 4 | import * as responseJson from './items-feed.spec.json'; 5 | 6 | describe('Items feed', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let response: IDeliveryNetworkResponse, any>; 11 | 12 | beforeAll(async () => { 13 | response = await getDeliveryClientWithJsonAndHeaders( 14 | responseJson, 15 | { 16 | environmentId: 'x', 17 | }, 18 | [ 19 | { 20 | value: 'TokenX', 21 | header: 'X-Continuation' 22 | } 23 | ] 24 | ) 25 | .itemsFeed() 26 | .toPromise(); 27 | }); 28 | 29 | it(`Continuation token should be set`, () => { 30 | expect(response.xContinuationToken).toEqual('TokenX'); 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/circular-referenced-linked-items-mapping.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "item": { 3 | "system": { 4 | "id": "117cdfae-52cf-4885-b271-66aef6825612", 5 | "name": "Coffee processing techniques", 6 | "codename": "coffee_processing_techniques", 7 | "language": "en-US", 8 | "type": "article", 9 | "sitemap_locations": [ 10 | "articles" 11 | ], 12 | "collection": "default", 13 | "last_modified": "2018-07-17T08:12:03.07Z" 14 | }, 15 | "elements": { 16 | "title": { 17 | "type": "text", 18 | "name": "Title", 19 | "value": "Coffee processing techniques" 20 | }, 21 | "related_articles": { 22 | "type": "modular_content", 23 | "name": "Related articles", 24 | "value": [ 25 | "on_roasts", 26 | "which_brewing_fits_you_" 27 | ] 28 | } 29 | } 30 | }, 31 | "modular_content": { 32 | "on_roasts": { 33 | "system": { 34 | "id": "f4b3fc05-e988-4dae-9ac1-a94aba566474", 35 | "name": "On Roasts", 36 | "codename": "on_roasts", 37 | "language": "en-US", 38 | "type": "article", 39 | "sitemap_locations": [ 40 | "articles" 41 | ], 42 | "collection": "default", 43 | "last_modified": "2017-12-08T08:49:53.603Z" 44 | }, 45 | "elements": { 46 | "title": { 47 | "type": "text", 48 | "name": "Title", 49 | "value": "On Roasts" 50 | }, 51 | "related_articles": { 52 | "type": "modular_content", 53 | "name": "Related articles", 54 | "value": [ 55 | "on_roasts", 56 | "which_brewing_fits_you_" 57 | ] 58 | } 59 | } 60 | }, 61 | "which_brewing_fits_you_": { 62 | "system": { 63 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 64 | "name": "Which brewing fits you?", 65 | "codename": "which_brewing_fits_you_", 66 | "language": "en-US", 67 | "type": "article", 68 | "sitemap_locations": [ 69 | "articles" 70 | ], 71 | "collection": "default", 72 | "last_modified": "2018-10-11T08:45:56.917Z" 73 | }, 74 | "elements": { 75 | "title": { 76 | "type": "text", 77 | "name": "Title", 78 | "value": "Which brewing fits you?" 79 | }, 80 | "related_articles": { 81 | "type": "modular_content", 82 | "name": "Related articles", 83 | "value": [ 84 | "coffee_processing_techniques", 85 | "on_roasts" 86 | ] 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/circular-referenced-linked-items-mapping.spec.ts: -------------------------------------------------------------------------------- 1 | import { getDeliveryClientWithJson } from '../../../setup'; 2 | import { Responses, Elements } from '../../../../../lib'; 3 | import * as responseJson from './circular-referenced-linked-items-mapping.spec.json'; 4 | 5 | describe('Circular references in linked items', () => { 6 | let response: Responses.IViewContentItemResponse; 7 | 8 | const modularItemElement = 'related_articles'; 9 | 10 | beforeAll(async () => { 11 | response = (await getDeliveryClientWithJson(responseJson).item('x').toPromise()).data; 12 | }); 13 | 14 | it(`Response should be defines and there should be no stackoverflow exception logged`, () => { 15 | expect(response).toBeDefined(); 16 | }); 17 | 18 | it(`'coffee_processing_techniques' should have mapped items`, () => { 19 | const linkedItem = response.item; 20 | expect(linkedItem).toBeDefined(); 21 | expect( 22 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 23 | (m) => m.system.codename === 'on_roasts' 24 | ) 25 | ).toBeDefined(); 26 | expect( 27 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 28 | (m) => m.system.codename === 'which_brewing_fits_you_' 29 | ) 30 | ).toBeDefined(); 31 | }); 32 | 33 | it(`'on_roasts' should have mapped items `, () => { 34 | const linkedItemCodename = 'on_roasts'; 35 | const linkedItem = response.linkedItems[linkedItemCodename]; 36 | 37 | expect(linkedItem).toBeDefined(); 38 | 39 | if (!linkedItem) { 40 | throw Error(`Item with codename '${linkedItemCodename}' was not found`); 41 | } 42 | 43 | expect( 44 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 45 | (m) => m.system.codename === 'on_roasts' 46 | ) 47 | ).toBeDefined(); 48 | expect( 49 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 50 | (m) => m.system.codename === 'which_brewing_fits_you_' 51 | ) 52 | ).toBeDefined(); 53 | }); 54 | 55 | it(`'which_brewing_fits_you_' should have mapped items`, () => { 56 | const linkedItemCodename = 'which_brewing_fits_you_'; 57 | const linkedItem = response.linkedItems[linkedItemCodename]; 58 | 59 | expect(linkedItem).toBeDefined(); 60 | 61 | if (!linkedItem) { 62 | throw Error(`Item with codename '${linkedItemCodename}' was not found`); 63 | } 64 | 65 | expect( 66 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 67 | (m) => m.system.codename === 'on_roasts' 68 | ) 69 | ).toBeDefined(); 70 | expect( 71 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 72 | (m) => m.system.codename === 'coffee_processing_techniques' 73 | ) 74 | ).toBeDefined(); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-list-response.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 6 | "name": "Which brewing fits you?", 7 | "codename": "which_brewing_fits_you_", 8 | "language": "en-US", 9 | "type": "article", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-03-27T13:24:54.042Z" 13 | }, 14 | "elements": { 15 | "title": { 16 | "type": "text", 17 | "name": "Title", 18 | "value": "Which brewing fits you?" 19 | }, 20 | "summary": { 21 | "type": "text", 22 | "name": "Summary", 23 | "value": "We have put down three procedures with clearly written steps describing the process of making coffee. Read this article to convince yourself that brewing coffee is no science." 24 | }, 25 | "post_date": { 26 | "type": "date_time", 27 | "name": "Post date", 28 | "value": "2014-10-27T00:00:00Z" 29 | } 30 | } 31 | } 32 | ], 33 | "modular_content": {}, 34 | "pagination": { 35 | "skip": 0, 36 | "limit": 0, 37 | "count": 1, 38 | "next_page": "" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-list-response.spec.ts: -------------------------------------------------------------------------------- 1 | import { IQueryParameter } from '@kontent-ai/core-sdk'; 2 | 3 | import { Responses } from '../../../../../lib'; 4 | import { Context, setup } from '../../../setup'; 5 | import { getDeliveryClientWithJson } from '../../../setup'; 6 | import * as responseJson from './items-list-response.spec.json'; 7 | 8 | describe('Items list response', () => { 9 | const context = new Context(); 10 | setup(context); 11 | 12 | let response: Responses.IListContentItemsResponse; 13 | const parameters: IQueryParameter[] = []; 14 | 15 | beforeAll(async () => { 16 | const query = getDeliveryClientWithJson(responseJson).items().includeTotalCountParameter(); 17 | parameters.push(...query.getParameters()); 18 | response = (await query.toPromise()).data; 19 | }); 20 | 21 | it(`Total count parameter should be set to true`, () => { 22 | const totalCountParameter = parameters.find( 23 | (m) => m.getParam().toLowerCase() === 'includeTotalCount=true'.toLowerCase() 24 | ); 25 | 26 | expect(totalCountParameter).toBeDefined(); 27 | }); 28 | 29 | it(`Response should have pagination`, () => { 30 | expect(response.pagination).toBeDefined(); 31 | }); 32 | 33 | it(`Response item should be mapped properly`, () => { 34 | const item = response.items[0]; 35 | const rawItem = responseJson.items[0]; 36 | 37 | expect(item).toBeDefined(); 38 | expect(rawItem).toBeDefined(); 39 | expect(item.system).toBeDefined(); 40 | 41 | expect(item.elements.title.value).toEqual(rawItem.elements.title.value); 42 | expect(item.elements.summary.value).toEqual(rawItem.elements.summary.value); 43 | expect(item.elements.post_date.value).toEqual(rawItem.elements.post_date.value); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-list-with-reserved-names-in-linked-items-response.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 6 | "name": "Brewing", 7 | "codename": "Brewing", 8 | "language": "en-US", 9 | "type": "article", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-03-27T13:24:54.042Z" 13 | }, 14 | "elements": { 15 | "title": { 16 | "type": "text", 17 | "name": "Title", 18 | "value": "Which brewing fits you?" 19 | }, 20 | "summary": { 21 | "type": "text", 22 | "name": "Summary", 23 | "value": "We have put down three procedures with clearly written steps describing the process of making coffee. Read this article to convince yourself that brewing coffee is no science." 24 | }, 25 | "post_date": { 26 | "type": "date_time", 27 | "name": "Post date", 28 | "value": "2014-10-27T00:00:00Z" 29 | }, 30 | "related_articles": { 31 | "type": "modular_content", 32 | "name": "Related articles", 33 | "value": ["constructor"] 34 | } 35 | } 36 | } 37 | ], 38 | "modular_content": { 39 | "constructor": { 40 | "system": { 41 | "id": "f4b3fc05-e988-4dae-9ac1-a94aba566474", 42 | "name": "On Roasts", 43 | "codename": "constructor", 44 | "language": "en-US", 45 | "type": "article", 46 | "sitemap_locations": ["articles"], 47 | "collection": "default", 48 | "last_modified": "2017-12-08T08:49:53.603Z" 49 | }, 50 | "elements": { 51 | "title": { 52 | "type": "text", 53 | "name": "Title", 54 | "value": "constructor" 55 | } 56 | } 57 | } 58 | }, 59 | "pagination": { 60 | "skip": 0, 61 | "limit": 0, 62 | "count": 1, 63 | "next_page": "" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-list-with-reserved-names-in-linked-items-response.spec.ts: -------------------------------------------------------------------------------- 1 | import { IQueryParameter } from '@kontent-ai/core-sdk'; 2 | 3 | import { Responses } from '../../../../../lib'; 4 | import { Context, setup } from '../../../setup'; 5 | import { getDeliveryClientWithJson } from '../../../setup'; 6 | import * as responseJson from './items-list-with-reserved-names-in-linked-items-response.spec.json'; 7 | 8 | describe('Items list with reserved names in linked items', () => { 9 | const context = new Context(); 10 | setup(context); 11 | 12 | let response: Responses.IListContentItemsResponse; 13 | const parameters: IQueryParameter[] = []; 14 | 15 | beforeAll(async () => { 16 | const query = getDeliveryClientWithJson(responseJson).items().includeTotalCountParameter(); 17 | parameters.push(...query.getParameters()); 18 | response = (await query.toPromise()).data; 19 | }); 20 | 21 | it(`Total count parameter should be set to true`, () => { 22 | const totalCountParameter = parameters.find( 23 | (m) => m.getParam().toLowerCase() === 'includeTotalCount=true'.toLowerCase() 24 | ); 25 | 26 | expect(totalCountParameter).toBeDefined(); 27 | }); 28 | 29 | it(`Response should have pagination`, () => { 30 | expect(response.pagination).toBeDefined(); 31 | }); 32 | 33 | it(`Response item should be mapped properly`, () => { 34 | const item = response.items[0]; 35 | const rawItem = responseJson.items[0]; 36 | 37 | expect(item).toBeDefined(); 38 | expect(rawItem).toBeDefined(); 39 | expect(item.system).toBeDefined(); 40 | 41 | expect(item.elements.title.value).toEqual(rawItem.elements.title.value); 42 | expect(item.elements.summary.value).toEqual(rawItem.elements.summary.value); 43 | expect(item.elements.post_date.value).toEqual(rawItem.elements.post_date.value); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-list-with-reserved-names-response.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 6 | "name": "constructor", 7 | "codename": "constructor", 8 | "language": "en-US", 9 | "type": "article", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-03-27T13:24:54.042Z" 13 | }, 14 | "elements": { 15 | "title": { 16 | "type": "text", 17 | "name": "Title", 18 | "value": "Which brewing fits you?" 19 | }, 20 | "summary": { 21 | "type": "text", 22 | "name": "Summary", 23 | "value": "We have put down three procedures with clearly written steps describing the process of making coffee. Read this article to convince yourself that brewing coffee is no science." 24 | }, 25 | "post_date": { 26 | "type": "date_time", 27 | "name": "Post date", 28 | "value": "2014-10-27T00:00:00Z" 29 | } 30 | } 31 | } 32 | ], 33 | "modular_content": { 34 | }, 35 | "pagination": { 36 | "skip": 0, 37 | "limit": 0, 38 | "count": 1, 39 | "next_page": "" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-list-with-reserved-names-response.spec.ts: -------------------------------------------------------------------------------- 1 | import { IQueryParameter } from '@kontent-ai/core-sdk'; 2 | 3 | import { Responses } from '../../../../../lib'; 4 | import { Context, setup } from '../../../setup'; 5 | import { getDeliveryClientWithJson } from '../../../setup'; 6 | import * as responseJson from './items-list-with-reserved-names-response.spec.json'; 7 | 8 | describe('Items list with reserved names', () => { 9 | const context = new Context(); 10 | setup(context); 11 | 12 | let response: Responses.IListContentItemsResponse; 13 | const parameters: IQueryParameter[] = []; 14 | 15 | beforeAll(async () => { 16 | const query = getDeliveryClientWithJson(responseJson).items().includeTotalCountParameter(); 17 | parameters.push(...query.getParameters()); 18 | response = (await query.toPromise()).data; 19 | }); 20 | 21 | it(`Total count parameter should be set to true`, () => { 22 | const totalCountParameter = parameters.find( 23 | (m) => m.getParam().toLowerCase() === 'includeTotalCount=true'.toLowerCase() 24 | ); 25 | 26 | expect(totalCountParameter).toBeDefined(); 27 | }); 28 | 29 | it(`Response should have pagination`, () => { 30 | expect(response.pagination).toBeDefined(); 31 | }); 32 | 33 | it(`Response item should be mapped properly`, () => { 34 | const item = response.items[0]; 35 | const rawItem = responseJson.items[0]; 36 | 37 | expect(item).toBeDefined(); 38 | expect(rawItem).toBeDefined(); 39 | expect(item.system).toBeDefined(); 40 | 41 | expect(item.elements.title.value).toEqual(rawItem.elements.title.value); 42 | expect(item.elements.summary.value).toEqual(rawItem.elements.summary.value); 43 | expect(item.elements.post_date.value).toEqual(rawItem.elements.post_date.value); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-with-total-count.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 6 | "name": "Which brewing fits you?", 7 | "codename": "which_brewing_fits_you_", 8 | "language": "en-US", 9 | "type": "article", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-03-27T13:24:54.042Z" 13 | }, 14 | "elements": { 15 | "title": { 16 | "type": "text", 17 | "name": "Title", 18 | "value": "Which brewing fits you?" 19 | }, 20 | "summary": { 21 | "type": "text", 22 | "name": "Summary", 23 | "value": "We have put down three procedures with clearly written steps describing the process of making coffee. Read this article to convince yourself that brewing coffee is no science." 24 | }, 25 | "post_date": { 26 | "type": "date_time", 27 | "name": "Post date", 28 | "value": "2014-10-27T00:00:00Z" 29 | } 30 | } 31 | } 32 | ], 33 | "modular_content": {}, 34 | "pagination": { 35 | "total_count": 1, 36 | "skip": 0, 37 | "limit": 0, 38 | "count": 1, 39 | "next_page": "" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-with-total-count.spec.ts: -------------------------------------------------------------------------------- 1 | import { IQueryParameter } from '@kontent-ai/core-sdk'; 2 | 3 | import { Responses } from '../../../../../lib'; 4 | import { Context, setup } from '../../../setup'; 5 | import { getDeliveryClientWithJson } from '../../../setup'; 6 | import * as responseJson from './items-with-total-count.spec.json'; 7 | 8 | describe('Items with total count', () => { 9 | const context = new Context(); 10 | setup(context); 11 | 12 | let response: Responses.IListContentItemsResponse; 13 | const parameters: IQueryParameter[] = []; 14 | 15 | beforeAll(async () => { 16 | const query = getDeliveryClientWithJson(responseJson).items().includeTotalCountParameter(); 17 | 18 | parameters.push(...query.getParameters()); 19 | 20 | response = (await query.toPromise()).data; 21 | }); 22 | 23 | it(`Total count parameter should be set to true`, () => { 24 | const totalCountParameter = parameters.find( 25 | (m) => m.getParam().toLowerCase() === 'includeTotalCount=true'.toLowerCase() 26 | ); 27 | 28 | expect(totalCountParameter).toBeDefined(); 29 | }); 30 | 31 | it(`Response should have total count parameter set`, () => { 32 | expect(response.pagination.totalCount).toEqual(responseJson.pagination.total_count); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-without-total-count.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 6 | "name": "Which brewing fits you?", 7 | "codename": "which_brewing_fits_you_", 8 | "language": "en-US", 9 | "type": "article", 10 | "sitemap_locations": [], 11 | "collection": "default", 12 | "last_modified": "2019-03-27T13:24:54.042Z" 13 | }, 14 | "elements": { 15 | "title": { 16 | "type": "text", 17 | "name": "Title", 18 | "value": "Which brewing fits you?" 19 | }, 20 | "summary": { 21 | "type": "text", 22 | "name": "Summary", 23 | "value": "We have put down three procedures with clearly written steps describing the process of making coffee. Read this article to convince yourself that brewing coffee is no science." 24 | }, 25 | "post_date": { 26 | "type": "date_time", 27 | "name": "Post date", 28 | "value": "2014-10-27T00:00:00Z" 29 | } 30 | } 31 | } 32 | ], 33 | "modular_content": {}, 34 | "pagination": { 35 | "skip": 0, 36 | "limit": 0, 37 | "count": 1, 38 | "next_page": "" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/items/items-without-total-count.spec.ts: -------------------------------------------------------------------------------- 1 | import { IQueryParameter } from '@kontent-ai/core-sdk'; 2 | 3 | import { Responses } from '../../../../../lib'; 4 | import { Context, setup } from '../../../setup'; 5 | import { getDeliveryClientWithJson } from '../../../setup'; 6 | import * as responseJson from './items-without-total-count.spec.json'; 7 | 8 | describe('Items without total count', () => { 9 | const context = new Context(); 10 | setup(context); 11 | 12 | let response: Responses.IListContentItemsResponse; 13 | const parameters: IQueryParameter[] = []; 14 | 15 | beforeAll(async () => { 16 | const query = getDeliveryClientWithJson(responseJson).items(); 17 | 18 | parameters.push(...query.getParameters()); 19 | 20 | response = (await query.toPromise()).data; 21 | }); 22 | 23 | it(`Total count parameter should not be set`, () => { 24 | const totalCountParameter = parameters.find((m) => 25 | m.getParam().toLowerCase().startsWith('includeTotalCount'.toLowerCase()) 26 | ); 27 | 28 | expect(totalCountParameter).toBeUndefined(); 29 | }); 30 | 31 | it(`Response should not have total count parameter set`, () => { 32 | expect(response.pagination.totalCount).toEqual(null); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/languages/list-languages.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../../lib'; 2 | import { Context, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJson } from '../../../setup'; 4 | import * as responseJson from './list-languages.spec.json'; 5 | 6 | describe('List languages', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let response: Responses.IListLanguagesResponse; 11 | 12 | beforeAll(async () => { 13 | response = (await getDeliveryClientWithJson(responseJson).languages().toPromise()).data; 14 | }); 15 | 16 | it(`Response should have all properties assigned`, () => { 17 | expect(response.items.length).toEqual(responseJson.languages.length); 18 | 19 | for (const item of response.items) { 20 | const originalItem = responseJson.languages.find((m) => m.system.id === item.system.id); 21 | 22 | if (!originalItem) { 23 | throw Error(`Invalid language '${item.system.id}'`); 24 | } 25 | 26 | expect(item.system.id).toEqual(originalItem.system.id); 27 | expect(item.system.name).toEqual(originalItem.system.name); 28 | expect(item.system.codename).toEqual(originalItem.system.codename); 29 | } 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/sync/initialize-sync.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "change_type": "deleted_item", 5 | "timestamp": "2022-10-20T13:03:06.1310204Z", 6 | "data": { 7 | "system": { 8 | "id": "a35d17ac-b6ba-4c6a-ae31-23c1193215cb", 9 | "collection": "default", 10 | "name": "My article", 11 | "codename": "my_article", 12 | "language": "en-US", 13 | "type": "article", 14 | "sitemap_locations": [], 15 | "last_modified": "2019-03-27T13:21:11.38Z", 16 | "workflow_step": "published" 17 | } 18 | } 19 | }, 20 | { 21 | "change_type": "changed_item", 22 | "timestamp": "2022-10-20T13:03:06.1310204Z", 23 | "data": { 24 | "system": { 25 | "id": "335d17ac-b6ba-4c6a-ae31-23c1193215cb", 26 | "collection": "default", 27 | "name": "My article", 28 | "codename": "my_article", 29 | "language": "en-US", 30 | "type": "article", 31 | "sitemap_locations": [], 32 | "last_modified": "2019-03-27T13:21:11.38Z", 33 | "workflow_step": "published" 34 | }, 35 | "elements": { 36 | "title": { 37 | "type": "text", 38 | "name": "Title", 39 | "value": "Warrior" 40 | } 41 | } 42 | } 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/sync/initialize-sync.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../../lib'; 2 | import { Context, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJson } from '../../../setup'; 4 | import * as responseJson from './initialize-sync.spec.json'; 5 | 6 | describe('Initialize sync', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let response: Responses.IInitializeSyncResponse; 11 | 12 | beforeAll(async () => { 13 | response = (await getDeliveryClientWithJson(responseJson).initializeSync().toPromise()).data; 14 | }); 15 | 16 | it(`Response should have all properties assigned`, () => { 17 | expect(response.items.length).toEqual(responseJson.items.length); 18 | 19 | for (const item of response.items) { 20 | const originalItem = responseJson.items.find((m) => m.data.system.id === item.data.system.id); 21 | 22 | if (!originalItem) { 23 | throw Error(`Invalid item '${item.data.system.id}'`); 24 | } 25 | 26 | expect(item.timestamp).toEqual(originalItem.timestamp); 27 | expect(item.changeType).toEqual(originalItem.change_type); 28 | 29 | expect(item.data.system.codename).toEqual(originalItem.data.system.codename); 30 | expect(item.data.system.type).toEqual(originalItem.data.system.type); 31 | expect(item.data.system.collection).toEqual(originalItem.data.system.collection); 32 | expect(item.data.system.language).toEqual(originalItem.data.system.language); 33 | expect(item.data.system.name).toEqual(originalItem.data.system.name); 34 | expect(item.data.system.workflowStep).toEqual(originalItem.data.system.workflow_step); 35 | expect(item.data.system.lastModified).toEqual(originalItem.data.system.last_modified); 36 | 37 | if (originalItem.data.elements) { 38 | expect(item.data.elements).toEqual(originalItem.data.elements); 39 | } else { 40 | expect(item.data.elements).toEqual({}); 41 | } 42 | } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/sync/sync-changes.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "change_type": "deleted_item", 5 | "timestamp": "2022-10-20T13:03:06.1310204Z", 6 | "data": { 7 | "system": { 8 | "id": "135d17ac-b6ba-4c6a-ae31-23c1193215cb", 9 | "collection": "default", 10 | "name": "My article", 11 | "codename": "my_article", 12 | "language": "en-US", 13 | "type": "article", 14 | "sitemap_locations": [], 15 | "last_modified": "2019-03-27T13:21:11.38Z", 16 | "workflow_step": "published" 17 | } 18 | } 19 | }, 20 | { 21 | "change_type": "changed_item", 22 | "timestamp": "2022-10-20T13:03:06.1310204Z", 23 | "data": { 24 | "system": { 25 | "id": "335d17ac-b6ba-4c6a-ae31-23c1193215cb", 26 | "collection": "default", 27 | "name": "My article", 28 | "codename": "my_article", 29 | "language": "en-US", 30 | "type": "article", 31 | "sitemap_locations": [], 32 | "last_modified": "2019-03-27T13:21:11.38Z", 33 | "workflow_step": "published" 34 | }, 35 | "elements": { 36 | "title": { 37 | "type": "text", 38 | "name": "Title", 39 | "value": "Warrior" 40 | } 41 | } 42 | } 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/sync/sync-changes.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../../lib'; 2 | import { Context, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJson } from '../../../setup'; 4 | import * as responseJson from './sync-changes.spec.json'; 5 | 6 | describe('Sync changes', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let response: Responses.ISyncChangesResponse; 11 | 12 | beforeAll(async () => { 13 | response = (await getDeliveryClientWithJson(responseJson).syncChanges().toPromise()).data; 14 | }); 15 | 16 | it(`Response should have all properties assigned`, () => { 17 | expect(response.items.length).toEqual(responseJson.items.length); 18 | 19 | for (const item of response.items) { 20 | const originalItem = responseJson.items.find((m) => m.data.system.id === item.data.system.id); 21 | 22 | if (!originalItem) { 23 | throw Error(`Invalid item '${item.data.system.id}'`); 24 | } 25 | 26 | expect(item.timestamp).toEqual(originalItem.timestamp); 27 | expect(item.changeType).toEqual(originalItem.change_type); 28 | 29 | expect(item.data.system.codename).toEqual(originalItem.data.system.codename); 30 | expect(item.data.system.type).toEqual(originalItem.data.system.type); 31 | expect(item.data.system.collection).toEqual(originalItem.data.system.collection); 32 | expect(item.data.system.language).toEqual(originalItem.data.system.language); 33 | expect(item.data.system.name).toEqual(originalItem.data.system.name); 34 | expect(item.data.system.workflowStep).toEqual(originalItem.data.system.workflow_step); 35 | expect(item.data.system.lastModified).toEqual(originalItem.data.system.last_modified); 36 | 37 | if (originalItem.data.elements) { 38 | expect(item.data.elements).toEqual(originalItem.data.elements); 39 | } else { 40 | expect(item.data.elements).toEqual({}); 41 | } 42 | } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/used-in/used-in-all.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "e27603e9-49e7-4d73-ba9f-843017d460db", 6 | "name": "Product", 7 | "codename": "product", 8 | "language": "default", 9 | "type": "product_example_content_type", 10 | "collection": "default", 11 | "workflow": "default", 12 | "workflow_step": "published", 13 | "last_modified": "2019-09-16T09:30:06.1123998Z" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/used-in/used-in-all.spec.ts: -------------------------------------------------------------------------------- 1 | import { IGroupedNetworkResponse, Responses } from '../../../../../lib'; 2 | import { Context, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJsonAndHeaders } from '../../../setup'; 4 | import * as responseJson from './used-in-all.spec.json'; 5 | 6 | describe('Used in', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let response: IGroupedNetworkResponse>; 11 | 12 | beforeAll(async () => { 13 | response = await getDeliveryClientWithJsonAndHeaders( 14 | responseJson, 15 | { 16 | environmentId: 'x' 17 | }, 18 | [] 19 | ) 20 | .itemUsedIn('x') 21 | .queryConfig({ 22 | disableItemLinking: false 23 | }) 24 | .toAllPromise(); 25 | }); 26 | 27 | it(`Validate responses counts`, () => { 28 | expect(response.responses.length).toEqual(1); 29 | }); 30 | 31 | it(`Validate items count`, () => { 32 | expect(response.data.items.length).toEqual(responseJson.items.length); 33 | }); 34 | 35 | it(`Validates mapped item`, () => { 36 | const item = response.data.items[0]; 37 | const sourceItem = responseJson.items[0]; 38 | 39 | expect(item.system.id).toEqual(sourceItem.system.id); 40 | expect(item.system.name).toEqual(sourceItem.system.name); 41 | expect(item.system.codename).toEqual(sourceItem.system.codename); 42 | expect(item.system.language).toEqual(sourceItem.system.language); 43 | expect(item.system.type).toEqual(sourceItem.system.type); 44 | expect(item.system.collection).toEqual(sourceItem.system.collection); 45 | expect(item.system.lastModified).toEqual(sourceItem.system.last_modified); 46 | expect(item.system.workflow).toEqual(sourceItem.system.workflow); 47 | expect(item.system.workflowStep).toEqual(sourceItem.system.workflow_step); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/used-in/used-in.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "system": { 5 | "id": "e27603e9-49e7-4d73-ba9f-843017d460db", 6 | "name": "Product", 7 | "codename": "product", 8 | "language": "default", 9 | "type": "product_example_content_type", 10 | "collection": "default", 11 | "workflow": "default", 12 | "workflow_step": "published", 13 | "last_modified": "2019-09-16T09:30:06.1123998Z" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/endpoints/used-in/used-in.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse, Responses } from '../../../../../lib'; 2 | import { Context, getTestDeliveryClient, setup } from '../../../setup'; 3 | import { getDeliveryClientWithJsonAndHeaders } from '../../../setup'; 4 | import * as responseJson from './used-in.spec.json'; 5 | 6 | describe('Used in', () => { 7 | const context = new Context(); 8 | setup(context); 9 | 10 | const query = getDeliveryClientWithJsonAndHeaders( 11 | responseJson, 12 | { 13 | environmentId: 'x' 14 | }, 15 | [ 16 | { 17 | value: 'TokenX', 18 | header: 'X-Continuation' 19 | } 20 | ] 21 | ).itemUsedIn('product'); 22 | 23 | let response: IDeliveryNetworkResponse, any>; 24 | 25 | beforeAll(async () => { 26 | response = await query.toPromise(); 27 | }); 28 | 29 | it(`Validates asset used in url`, () => { 30 | expect(getTestDeliveryClient().assetUsedIn('x').getUrl()).toEqual( 31 | 'https://deliver.kontent.ai/delivery-environment-id/assets/x/used-in' 32 | ); 33 | }); 34 | 35 | it(`Validates content item used in url`, () => { 36 | expect(getTestDeliveryClient().itemUsedIn('x').getUrl()).toEqual( 37 | 'https://deliver.kontent.ai/delivery-environment-id/items/x/used-in' 38 | ); 39 | }); 40 | 41 | it(`Continuation token should be set`, () => { 42 | expect(response.xContinuationToken).toEqual('TokenX'); 43 | }); 44 | 45 | it(`Validates mapped item`, () => { 46 | const item = response.data.items[0]; 47 | const sourceItem = responseJson.items[0]; 48 | 49 | expect(item.system.id).toEqual(sourceItem.system.id); 50 | expect(item.system.name).toEqual(sourceItem.system.name); 51 | expect(item.system.codename).toEqual(sourceItem.system.codename); 52 | expect(item.system.language).toEqual(sourceItem.system.language); 53 | expect(item.system.type).toEqual(sourceItem.system.type); 54 | expect(item.system.collection).toEqual(sourceItem.system.collection); 55 | expect(item.system.lastModified).toEqual(sourceItem.system.last_modified); 56 | expect(item.system.workflow).toEqual(sourceItem.system.workflow); 57 | expect(item.system.workflowStep).toEqual(sourceItem.system.workflow_step); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/http/custom-http-service.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IResponse, 3 | IHttpDeleteQueryCall, 4 | IHttpGetQueryCall, 5 | IHttpPatchQueryCall, 6 | IHttpPostQueryCall, 7 | IHttpPutQueryCall, 8 | IHttpQueryOptions, 9 | IHttpService, 10 | IHttpCancelRequestToken 11 | } from '@kontent-ai/core-sdk'; 12 | 13 | import { DeliveryClient } from '../../../../lib'; 14 | 15 | class CustomHttpService implements IHttpService { 16 | getAsync(call: IHttpGetQueryCall, options?: IHttpQueryOptions): Promise> { 17 | return this.resolveTestCall(); 18 | } 19 | 20 | postAsync( 21 | call: IHttpPostQueryCall, 22 | options?: IHttpQueryOptions 23 | ): Promise> { 24 | return this.resolveTestCall(); 25 | } 26 | 27 | putAsync(call: IHttpPutQueryCall, options?: IHttpQueryOptions): Promise> { 28 | return this.resolveTestCall(); 29 | } 30 | 31 | patchAsync( 32 | call: IHttpPatchQueryCall, 33 | options?: IHttpQueryOptions 34 | ): Promise> { 35 | return this.resolveTestCall(); 36 | } 37 | 38 | deleteAsync( 39 | call: IHttpDeleteQueryCall, 40 | options?: IHttpQueryOptions 41 | ): Promise> { 42 | return this.resolveTestCall(); 43 | } 44 | 45 | createCancelToken(): IHttpCancelRequestToken { 46 | return { 47 | cancel: () => {}, 48 | token: undefined 49 | }; 50 | } 51 | 52 | private resolveTestCall(): Promise> { 53 | const promise = new Promise>((resolve, reject) => { 54 | resolve({ 55 | data: {}, 56 | headers: [], 57 | rawResponse: {}, 58 | retryStrategy: { 59 | options: {}, 60 | retryAttempts: 3 61 | }, 62 | status: 200 63 | }); 64 | }); 65 | return promise; 66 | } 67 | } 68 | 69 | describe('Custom Http service', () => { 70 | const client = new DeliveryClient({ 71 | environmentId: 'xxx', 72 | httpService: new CustomHttpService() 73 | }); 74 | 75 | it(`Should use custom http service`, () => { 76 | // this is fragile, but we don't want to expose these properties for now 77 | const httpService = client['queryService']['httpService']; 78 | expect(httpService).toEqual(jasmine.any(CustomHttpService)); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/query/custom-headers.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Custom query headers', () => { 4 | const context = new Context(); 5 | setup(context); 6 | 7 | it(`Item query initialization with invalid codename should throw Error`, () => { 8 | expect(() => context.deliveryClient.item(null as any)).toThrowError(); 9 | }); 10 | 11 | it(`Header should be present in result`, () => { 12 | const headers = context.deliveryClient 13 | .items() 14 | .queryConfig({ 15 | customHeaders: [ 16 | { 17 | header: 'xHeader', 18 | value: 'yValue' 19 | } 20 | ] 21 | }) 22 | .getHeaders(); 23 | 24 | const itemHeader = headers.find( 25 | m => m.header === 'xHeader' && m.value === 'yValue' 26 | ); 27 | 28 | expect(itemHeader).toBeDefined(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/query/query-config.spec.ts: -------------------------------------------------------------------------------- 1 | import { IQueryConfig, IItemQueryConfig } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Query configurations', () => { 5 | 6 | const context = new Context(); 7 | setup(context); 8 | 9 | it(`QueryConfig should set values properly`, () => { 10 | let queryConfig: IQueryConfig = { usePreviewMode: true, waitForLoadingNewContent: true }; 11 | expect(queryConfig.usePreviewMode).toEqual(true); 12 | expect(queryConfig.waitForLoadingNewContent).toEqual(true); 13 | 14 | queryConfig = ({ usePreviewMode: false, waitForLoadingNewContent: false }); 15 | expect(queryConfig.usePreviewMode).toEqual(false); 16 | expect(queryConfig.waitForLoadingNewContent).toEqual(false); 17 | }); 18 | 19 | it(`ItemQuerConfig should set values properly`, () => { 20 | let queryConfig: IItemQueryConfig = { usePreviewMode: true, waitForLoadingNewContent: true }; 21 | expect(queryConfig.usePreviewMode).toEqual(true); 22 | expect(queryConfig.waitForLoadingNewContent).toEqual(true); 23 | 24 | queryConfig = { usePreviewMode: false, waitForLoadingNewContent: false }; 25 | expect(queryConfig.usePreviewMode).toEqual(false); 26 | expect(queryConfig.waitForLoadingNewContent).toEqual(false); 27 | }); 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/query/query-custom-parameter.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Query custom parameter', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | it(`Url should contain the custom parameter with value #1`, () => { 9 | const url = context.deliveryClient.items().withCustomParameter('customParam', 'customVal').getUrl(); 10 | expect(url).toContain(`customParam=customVal`); 11 | }); 12 | 13 | it(`Url should contain the custom parameter with value #2`, () => { 14 | const url = context.deliveryClient.element('el', 'cd').withCustomParameter('customParam', 'customVal').getUrl(); 15 | expect(url).toContain(`customParam=customVal`); 16 | }); 17 | 18 | it(`Custom param with invalid name should throw Error`, () => { 19 | expect(() => context.deliveryClient.items().withCustomParameter(undefined as any, 'customVal')).toThrowError(); 20 | expect(() => context.deliveryClient.items().withCustomParameter(null as any, 'customVal')).toThrowError(); 21 | }); 22 | 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/query/query-initialization.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Query initialization', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | it(`Element query initialization with invalid type should throw Error`, () => { 9 | expect(() => context.deliveryClient.element(null as any, 'title')).toThrowError(); 10 | }); 11 | 12 | it(`Element query initialization with invalid type should throw Error`, () => { 13 | expect(() => context.deliveryClient.element('movie', null as any)).toThrowError(); 14 | }); 15 | 16 | it(`Item query initialization with invalid codename should throw Error`, () => { 17 | expect(() => context.deliveryClient.item(null as any)).toThrowError(); 18 | }); 19 | 20 | it(`Type query initialization with invalid codename should throw Error`, () => { 21 | expect(() => context.deliveryClient.type(null as any)).toThrowError(); 22 | }); 23 | 24 | it(`Taxonomy query initialization with invalid codename should throw Error`, () => { 25 | expect(() => context.deliveryClient.taxonomy(null as any)).toThrowError(); 26 | }); 27 | 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/query/query-url-debug.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Query url debug', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | it(`Item url`, () => { 9 | const url = context.deliveryClient.item('warrior').getUrl(); 10 | expect(url).toContain('items/warrior'); 11 | }); 12 | 13 | it(`Items url`, () => { 14 | const url = context.deliveryClient.items().getUrl(); 15 | expect(url).toContain('items'); 16 | }); 17 | 18 | it(`Taxonomy url`, () => { 19 | const url = context.deliveryClient.taxonomy('taxonomy_element').getUrl(); 20 | expect(url).toContain('taxonomies/taxonomy_element'); 21 | }); 22 | 23 | it(`Taxonomies url`, () => { 24 | const url = context.deliveryClient.taxonomies().getUrl(); 25 | expect(url).toContain('taxonomies'); 26 | }); 27 | 28 | it(`Type url`, () => { 29 | const url = context.deliveryClient.type('movie').getUrl(); 30 | expect(url).toContain('types/movie'); 31 | }); 32 | 33 | it(`Types url`, () => { 34 | const url = context.deliveryClient.types().getUrl(); 35 | expect(url).toContain('types'); 36 | }); 37 | 38 | it(`Element url`, () => { 39 | const url = context.deliveryClient.element('movie', 'title').getUrl(); 40 | expect(url).toContain('types/movie/elements/title'); 41 | }); 42 | 43 | }); 44 | 45 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/query/wait-for-loading-new-content.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Wait for loading new content', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const waitForLoadingNewContentHeader: string = 'X-KC-Wait-For-Loading-New-Content'; 9 | 10 | it(`'X-KC-Wait-For-Loading-New-Content' header should NOT be set when Query configuration does not set it`, () => { 11 | const itemHeaders = context.deliveryClient.item('any').getHeaders(); 12 | const itemsHeaders = context.deliveryClient.items().getHeaders(); 13 | const taxonomyHeaders = context.deliveryClient.taxonomy('any').getHeaders(); 14 | const taxonomiesHeaders = context.deliveryClient.taxonomies().getHeaders(); 15 | const typeHeaders = context.deliveryClient.type('any').getHeaders(); 16 | const typesHeaders = context.deliveryClient.types().getHeaders(); 17 | 18 | const itemHeader = itemHeaders.find(m => m.header === waitForLoadingNewContentHeader); 19 | const itemsHeader = itemsHeaders.find(m => m.header === waitForLoadingNewContentHeader); 20 | const taxonomyHeader = taxonomyHeaders.find(m => m.header === waitForLoadingNewContentHeader); 21 | const taxonomiesHeader = taxonomiesHeaders.find(m => m.header === waitForLoadingNewContentHeader); 22 | const typeHeader = typeHeaders.find(m => m.header === waitForLoadingNewContentHeader); 23 | const typesHeader = typesHeaders.find(m => m.header === waitForLoadingNewContentHeader); 24 | 25 | expect(itemHeader).toBeUndefined(); 26 | expect(itemsHeader).toBeUndefined(); 27 | expect(taxonomyHeader).toBeUndefined(); 28 | expect(taxonomiesHeader).toBeUndefined(); 29 | expect(typeHeader).toBeUndefined(); 30 | expect(typesHeader).toBeUndefined(); 31 | }); 32 | 33 | it(`'X-KC-Wait-For-Loading-New-Content' header should be be set when Query configuration enables it`, () => { 34 | const itemHeaders = context.deliveryClient.item('any').queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 35 | const itemsHeaders = context.deliveryClient.items().queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 36 | const taxonomyHeaders = context.deliveryClient.taxonomy('any').queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 37 | const taxonomiesHeaders = context.deliveryClient.taxonomies().queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 38 | const typeHeaders = context.deliveryClient.type('any').queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 39 | const typesHeaders = context.deliveryClient.types().queryConfig({ waitForLoadingNewContent: true }).getHeaders(); 40 | 41 | const itemHeader = itemHeaders.find(m => m.header === waitForLoadingNewContentHeader); 42 | const itemsHeader = itemsHeaders.find(m => m.header === waitForLoadingNewContentHeader); 43 | const taxonomyHeader = taxonomyHeaders.find(m => m.header === waitForLoadingNewContentHeader); 44 | const taxonomiesHeader = taxonomiesHeaders.find(m => m.header === waitForLoadingNewContentHeader); 45 | const typeHeader = typeHeaders.find(m => m.header === waitForLoadingNewContentHeader); 46 | const typesHeader = typesHeaders.find(m => m.header === waitForLoadingNewContentHeader); 47 | 48 | expect(itemHeader).toBeDefined(); 49 | expect(itemsHeader).toBeDefined(); 50 | expect(taxonomyHeader).toBeDefined(); 51 | expect(taxonomiesHeader).toBeDefined(); 52 | expect(typeHeader).toBeDefined(); 53 | expect(typesHeader).toBeDefined(); 54 | }); 55 | }); 56 | 57 | 58 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/response/linked-items-mapping/linked-items-mapping-disabled.spec.ts: -------------------------------------------------------------------------------- 1 | import { Elements, Responses } from '../../../../../lib'; 2 | import { getTestDeliveryClient } from '../../../setup'; 3 | import * as responseJson from './linked-items-mapping.json'; 4 | 5 | describe('Linked items mapping disabled', () => { 6 | const client = getTestDeliveryClient({ 7 | environmentId: 'x', 8 | linkedItemsReferenceHandler: 'ignore' 9 | }); 10 | 11 | const response: Responses.IViewContentItemResponse = client.mappingService.viewContentItemResponse(responseJson); 12 | const modularItemElement = 'related_articles'; 13 | 14 | it(`'coffee_processing_techniques' should have mapped items`, () => { 15 | const linkedItem = response.item; 16 | expect(linkedItem).toBeDefined(); 17 | expect( 18 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 19 | (m) => m.system.codename === 'on_roasts' 20 | ) 21 | ).toBeUndefined(); 22 | expect( 23 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 24 | (m) => m.system.codename === 'which_brewing_fits_you_' 25 | ) 26 | ).toBeUndefined(); 27 | }); 28 | 29 | it(`'on_roasts' should not have parent object in its own linked items `, () => { 30 | const linkedItemCodename = 'on_roasts'; 31 | const linkedItem = response.linkedItems[linkedItemCodename]; 32 | 33 | expect(linkedItem).toBeDefined(); 34 | 35 | if (!linkedItem) { 36 | throw Error(`Item with codename '${linkedItemCodename}' was not found`); 37 | } 38 | 39 | expect( 40 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 41 | (m) => m.system.codename === 'on_roasts' 42 | ) 43 | ).toBeUndefined(); 44 | expect( 45 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 46 | (m) => m.system.codename === 'which_brewing_fits_you_' 47 | ) 48 | ).toBeUndefined(); 49 | }); 50 | 51 | it(`'which_brewing_fits_you_' should not have parent object in its own linked items `, () => { 52 | const linkedItemCodename = 'which_brewing_fits_you_'; 53 | const linkedItem = response.linkedItems[linkedItemCodename]; 54 | 55 | expect(linkedItem).toBeDefined(); 56 | 57 | if (!linkedItem) { 58 | throw Error(`Item with codename '${linkedItemCodename}' was not found`); 59 | } 60 | 61 | expect( 62 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 63 | (m) => m.system.codename === 'on_roasts' 64 | ) 65 | ).toBeUndefined(); 66 | expect( 67 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 68 | (m) => m.system.codename === 'coffee_processing_techniques' 69 | ) 70 | ).toBeUndefined(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/response/linked-items-mapping/linked-items-mapping-enabled.spec.ts: -------------------------------------------------------------------------------- 1 | import { Elements, Responses } from '../../../../../lib'; 2 | import { getTestDeliveryClient } from '../../../setup'; 3 | import * as responseJson from './linked-items-mapping.json'; 4 | 5 | describe('Linked items mapping enabled', () => { 6 | const client = getTestDeliveryClient({ 7 | environmentId: 'x', 8 | linkedItemsReferenceHandler: 'map' 9 | }); 10 | 11 | const response: Responses.IViewContentItemResponse = client.mappingService.viewContentItemResponse(responseJson); 12 | const modularItemElement = 'related_articles'; 13 | 14 | it(`'coffee_processing_techniques' should have mapped items`, () => { 15 | const linkedItem = response.item; 16 | expect(linkedItem).toBeDefined(); 17 | expect( 18 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 19 | (m) => m.system.codename === 'on_roasts' 20 | ) 21 | ).toBeDefined(); 22 | expect( 23 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 24 | (m) => m.system.codename === 'which_brewing_fits_you_' 25 | ) 26 | ).toBeDefined(); 27 | }); 28 | 29 | it(`'on_roasts' should have mapped items `, () => { 30 | const linkedItemCodename = 'on_roasts'; 31 | const linkedItem = response.linkedItems[linkedItemCodename]; 32 | 33 | expect(linkedItem).toBeDefined(); 34 | 35 | if (!linkedItem) { 36 | throw Error(`Item with codename '${linkedItemCodename}' was not found`); 37 | } 38 | 39 | expect( 40 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 41 | (m) => m.system.codename === 'on_roasts' 42 | ) 43 | ).toBeDefined(); 44 | expect( 45 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 46 | (m) => m.system.codename === 'which_brewing_fits_you_' 47 | ) 48 | ).toBeDefined(); 49 | }); 50 | 51 | it(`'which_brewing_fits_you_' should have mapped items`, () => { 52 | const linkedItemCodename = 'which_brewing_fits_you_'; 53 | const linkedItem = response.linkedItems[linkedItemCodename]; 54 | 55 | expect(linkedItem).toBeDefined(); 56 | 57 | if (!linkedItem) { 58 | throw Error(`Item with codename '${linkedItemCodename}' was not found`); 59 | } 60 | 61 | expect( 62 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 63 | (m) => m.system.codename === 'on_roasts' 64 | ) 65 | ).toBeDefined(); 66 | expect( 67 | (linkedItem.elements[modularItemElement] as Elements.LinkedItemsElement).linkedItems.find( 68 | (m) => m.system.codename === 'coffee_processing_techniques' 69 | ) 70 | ).toBeDefined(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/response/linked-items-mapping/linked-items-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "item": { 3 | "system": { 4 | "id": "117cdfae-52cf-4885-b271-66aef6825612", 5 | "name": "Coffee processing techniques", 6 | "codename": "coffee_processing_techniques", 7 | "language": "en-US", 8 | "type": "article", 9 | "sitemap_locations": [ 10 | "articles" 11 | ], 12 | "collection": "default", 13 | "last_modified": "2018-07-17T08:12:03.07Z", 14 | "workflow_step": "x", 15 | "workflow": "default" 16 | }, 17 | "elements": { 18 | "title": { 19 | "type": "text", 20 | "name": "Title", 21 | "value": "Coffee processing techniques" 22 | }, 23 | "related_articles": { 24 | "type": "modular_content", 25 | "name": "Related articles", 26 | "value": [ 27 | "on_roasts", 28 | "which_brewing_fits_you_" 29 | ] 30 | } 31 | } 32 | }, 33 | "modular_content": { 34 | "on_roasts": { 35 | "system": { 36 | "id": "f4b3fc05-e988-4dae-9ac1-a94aba566474", 37 | "name": "On Roasts", 38 | "codename": "on_roasts", 39 | "language": "en-US", 40 | "type": "article", 41 | "sitemap_locations": ["articles"], 42 | "collection": "default", 43 | "last_modified": "2017-12-08T08:49:53.603Z", 44 | "workflow_step": "x", 45 | "workflow": "default" 46 | }, 47 | "elements": { 48 | "title": { 49 | "type": "text", 50 | "name": "Title", 51 | "value": "On Roasts" 52 | }, 53 | "related_articles": { 54 | "type": "modular_content", 55 | "name": "Related articles", 56 | "value": [ 57 | "on_roasts", 58 | "which_brewing_fits_you_" 59 | ] 60 | } 61 | } 62 | }, 63 | "which_brewing_fits_you_": { 64 | "system": { 65 | "id": "3120ec15-a4a2-47ec-8ccd-c85ac8ac5ba5", 66 | "name": "Which brewing fits you?", 67 | "codename": "which_brewing_fits_you_", 68 | "language": "en-US", 69 | "type": "article", 70 | "sitemap_locations": ["articles"], 71 | "collection": "default", 72 | "last_modified": "2018-10-11T08:45:56.917Z", 73 | "workflow_step": "x", 74 | "workflow": "default" 75 | }, 76 | "elements": { 77 | "title": { 78 | "type": "text", 79 | "name": "Title", 80 | "value": "Which brewing fits you?" 81 | }, 82 | "related_articles": { 83 | "type": "modular_content", 84 | "name": "Related articles", 85 | "value": [ 86 | "coffee_processing_techniques", 87 | "on_roasts" 88 | ] 89 | } 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /test/browser/isolated-tests/services/generic-element-mapper.spec.ts: -------------------------------------------------------------------------------- 1 | import { GenericElementMapper } from '../../../../lib/mappers'; 2 | 3 | describe('GenericElementMapper', () => { 4 | 5 | const genericElementMapper = new GenericElementMapper(); 6 | 7 | it(`should throw an Error when invalid response is given`, () => { 8 | expect(() => genericElementMapper.mapElement(null as any)).toThrowError(); 9 | expect(() => genericElementMapper.mapElement(undefined as any)).toThrowError(); 10 | }); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/services/mapping-service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { getTestDeliveryClient } from '../../setup'; 3 | import * as responseJson from './mapping-service.spec.json'; 4 | 5 | describe('Mapping service', () => { 6 | const client = getTestDeliveryClient({ 7 | environmentId: 'x' 8 | }); 9 | 10 | const listingResponse: Responses.IListContentItemsResponse = 11 | client.mappingService.listContentItemsResponse(responseJson); 12 | 13 | it(`Listing response should be mapped correctly`, () => { 14 | expect(listingResponse.items.length).toEqual(responseJson.items.length); 15 | 16 | for (const item of listingResponse.items) { 17 | const rawItem = responseJson.items.find((m) => m.system.id === item.system.id); 18 | if (!rawItem) { 19 | throw Error(`Raw item with id '${item.system.id}' could not be found`); 20 | } 21 | 22 | expect(item.system.id).toEqual(rawItem.system.id); 23 | expect(item.system.codename).toEqual(rawItem.system.codename); 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/services/taxonomy-mapper.spec.ts: -------------------------------------------------------------------------------- 1 | import { TaxonomyMapper } from '../../../../lib/mappers'; 2 | 3 | describe('TaxonomyMapper', () => { 4 | 5 | const taxonomyMapper = new TaxonomyMapper(); 6 | 7 | it(`should throw an Error when invalid response is given`, () => { 8 | expect(() => taxonomyMapper.mapTaxonomy(null as any, [])).toThrowError(); 9 | expect(() => taxonomyMapper.mapTaxonomy(undefined as any, [])).toThrowError(); 10 | 11 | expect(() => taxonomyMapper.mapTaxonomy({} as any, null as any)).toThrowError(); 12 | expect(() => taxonomyMapper.mapTaxonomy({} as any, undefined as any)).toThrowError(); 13 | 14 | expect(() => taxonomyMapper.mapTaxonomy({} as any, 'test' as any)).toThrowError(); 15 | 16 | expect(() => taxonomyMapper.mapTaxonomies(null as any)).toThrowError(); 17 | expect(() => taxonomyMapper.mapTaxonomies(undefined as any)).toThrowError(); 18 | 19 | expect(() => taxonomyMapper.mapTaxonomies('test' as any)).toThrowError(); 20 | 21 | }); 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/services/type-mapper.spec.ts: -------------------------------------------------------------------------------- 1 | import { TypeMapper } from '../../../../lib/'; 2 | 3 | describe('TypeMapper', () => { 4 | 5 | const typeMapper = new TypeMapper(); 6 | 7 | it(`should throw an Error when invalid response is given`, () => { 8 | expect(() => typeMapper.mapSingleType(null as any)).toThrowError(); 9 | expect(() => typeMapper.mapSingleType(undefined as any)).toThrowError(); 10 | 11 | expect(() => typeMapper.mapSingleType({} as any)).toThrowError(); 12 | 13 | expect(() => typeMapper.mapMultipleTypes(null as any)).toThrowError(); 14 | expect(() => typeMapper.mapMultipleTypes(undefined as any)).toThrowError(); 15 | }); 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/url/item-url.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Item URL', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | it(`item query should thrown error when item's codename is not set`, () => { 9 | expect(() => context.deliveryClient.item(null as any)).toThrowError(); 10 | }); 11 | 12 | it(`item query should thrown error when item's codename is empty`, () => { 13 | expect(() => context.deliveryClient.item('')).toThrowError(); 14 | }); 15 | 16 | it(`item url with 'kyle' codename should end with '/items/kyle`, () => { 17 | const url = new URL(context.deliveryClient.item('kyle').getUrl()); 18 | expect(url.toString()).toContain(`/items/kyle`); 19 | }); 20 | 21 | it(`item url with 'arnold' codename should end with '/items/arnold'`, () => { 22 | const url = new URL(context.deliveryClient.item('arnold').getUrl()); 23 | expect(url.toString()).toContain(`/items/arnold`); 24 | }); 25 | }); 26 | 27 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/url/taxonomies-url.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Taxonomy url', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | it(`taxonomies url should contain skip parameter`, () => { 9 | const skip: number = 549228429; 10 | const url = context.deliveryClient.taxonomies().skipParameter(skip).getUrl(); 11 | expect(url).toContain('skip=' + skip.toString()); 12 | }); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/browser/isolated-tests/url/type-url.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | 3 | describe('Type url', () => { 4 | 5 | const context = new Context(); 6 | setup(context); 7 | 8 | it(`type query should thrown error when types's codename is not set`, () => { 9 | expect(() => context.deliveryClient.type(null as any)).toThrowError(); 10 | }); 11 | 12 | it(`type url with 'movie' codename should contain '/types/movie`, () => { 13 | const url = context.deliveryClient.type('movie').getUrl(); 14 | expect(url).toContain(`/types/movie`); 15 | }); 16 | 17 | it(`type url for all types should end with 'types'`, () => { 18 | const url = context.deliveryClient.types().getUrl(); 19 | expect(url).toContain(`/types`); 20 | }); 21 | 22 | it(`type url should contain skip parameter`, () => { 23 | const skip: number = 549228429; 24 | const url = context.deliveryClient.types().skipParameter(skip).getUrl(); 25 | expect(url).toContain('skip=' + skip.toString()); 26 | }); 27 | 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /test/browser/live-tests/custom-url/custom-url.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Custom URL', () => { 5 | const itemsUrl: string = 'https://deliver.kontent.ai/da5abe9f-fdad-4168-97cd-b3464be2ccb9/items?system.type=movie'; 6 | 7 | const context = new Context(); 8 | setup(context); 9 | 10 | let responseItems: Responses.IListContentItemsResponse; 11 | 12 | beforeAll(async () => { 13 | responseItems = (await context.deliveryClient.items().withCustomUrl(itemsUrl).toPromise()).data; 14 | }); 15 | 16 | it(`items should be defined`, () => { 17 | expect(responseItems).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/browser/live-tests/element/live-element.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Live element', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const typeCodename: string = 'movie'; 9 | 10 | const multipleChoiceElementCodename: string = 'category'; 11 | const taxonomyElementCodename: string = 'releasecategory'; 12 | 13 | let multipleChoiceElementResponse: Responses.IViewContentTypeElementResponse; 14 | let taxonomyElementResponse: Responses.IViewContentTypeElementResponse; 15 | 16 | beforeAll(async () => { 17 | multipleChoiceElementResponse = ( 18 | await context.deliveryClient.element(typeCodename, multipleChoiceElementCodename).toPromise() 19 | ).data; 20 | 21 | taxonomyElementResponse = ( 22 | await context.deliveryClient.element(typeCodename, taxonomyElementCodename).toPromise() 23 | ).data; 24 | }); 25 | 26 | it(`element responses should be defined`, () => { 27 | expect(multipleChoiceElementResponse).toBeDefined(); 28 | expect(taxonomyElementResponse).toBeDefined(); 29 | }); 30 | 31 | it(`element taxonomy element should contain valid taxonomy group property`, () => { 32 | expect(taxonomyElementResponse.taxonomyGroup).toBeDefined(); 33 | expect(taxonomyElementResponse.taxonomyGroup).toEqual(jasmine.any(String)); 34 | }); 35 | 36 | it(`multiple choice element should contain options`, () => { 37 | expect(multipleChoiceElementResponse.options).toBeDefined(); 38 | expect(multipleChoiceElementResponse.options.length).toBeGreaterThan(0); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/browser/live-tests/errors/kontent-error.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, setup } from '../../setup'; 2 | import { DeliveryError } from '../../../../lib'; 3 | 4 | describe('Delivery errors', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const invalidCodename: string = 'the_invalid_codename'; 9 | let succeeded: boolean; 10 | let error: unknown; 11 | 12 | beforeAll(async () => { 13 | try { 14 | await context.deliveryClient.item(invalidCodename).toPromise(); 15 | succeeded = true; 16 | } catch (err) { 17 | error = err; 18 | succeeded = false; 19 | } 20 | }); 21 | 22 | it(`Response shouldn't succeed because the item does not exists`, () => { 23 | expect(succeeded).toEqual(false); 24 | }); 25 | 26 | it(`Error should be an instance of DeliveryError`, () => { 27 | expect(error).toEqual(jasmine.any(DeliveryError)); 28 | }); 29 | 30 | it(`Error model should have all properties assigned`, () => { 31 | const baseError = error as DeliveryError; 32 | expect(baseError.errorCode).toBeGreaterThan(0); 33 | expect(baseError.specificCode).toBeGreaterThanOrEqual(0); 34 | expect(baseError.message).toBeDefined(); 35 | expect(baseError.requestId).toBeDefined(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/browser/live-tests/item/live-item-base-linked-items.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Live item - base linked items', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const movieCodename: string = 'warrior'; 9 | let response: Responses.IViewContentItemResponse; 10 | 11 | beforeAll(async () => { 12 | response = (await context.deliveryClient.item(movieCodename).toPromise()).data; 13 | }); 14 | 15 | it(`verify linked items included in response and are of 'ContentItem' type`, () => { 16 | expect(Object.keys(response.linkedItems).length).toEqual(3); 17 | 18 | for (const key of Object.keys(response.linkedItems)) { 19 | const linkedItem = response.linkedItems[key]; 20 | expect(linkedItem).toBeDefined(); 21 | } 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/browser/live-tests/item/live-item.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Live item', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const movieCodename: string = 'warrior'; 9 | let response: Responses.IViewContentItemResponse; 10 | 11 | beforeAll(async () => { 12 | response = (await context.deliveryClient.item(movieCodename).queryConfig({}).toPromise()).data; 13 | }); 14 | 15 | it(`item response should be defined`, () => { 16 | expect(response).toBeDefined(); 17 | }); 18 | 19 | it(`item should be defined`, () => { 20 | expect(response.item).toBeDefined(); 21 | }); 22 | 23 | it(`title should be 'Warrior'`, () => { 24 | expect(response.item.elements.title.value).toEqual('Warrior'); 25 | }); 26 | 27 | it(`released date should be '2011-09-09T00:00:00Z'`, () => { 28 | expect(response.item.elements.released.value).toEqual('2011-09-09T00:00:00Z'); 29 | }); 30 | 31 | it(`poster asset should be defined`, () => { 32 | expect(response.item.elements.poster).toBeDefined(); 33 | }); 34 | 35 | it(`poster asset' url should be set`, () => { 36 | const assetUrl = response.item.elements.poster.value[0].url; 37 | expect(assetUrl).toBeDefined(); 38 | expect(assetUrl).toContain('https://'); 39 | }); 40 | 41 | it(`category options should be defined`, () => { 42 | expect(response.item.elements.category.value).toBeDefined(); 43 | }); 44 | 45 | it(`there should be 2 category options defined`, () => { 46 | expect(response.item.elements.category.value.length).toEqual(2); 47 | }); 48 | 49 | it(`checks codename of first category option`, () => { 50 | expect(response.item.elements.category.value[0].codename).toEqual('action'); 51 | }); 52 | 53 | it(`checks codename of second category option`, () => { 54 | expect(response.item.elements.category.value[1].codename).toEqual('drama'); 55 | }); 56 | 57 | it(`checks that category options are of proper type`, () => { 58 | expect(response.item.elements.category.value[1]).toBeDefined(); 59 | }); 60 | 61 | it(`stars linked items should be defined`, () => { 62 | expect(response.item.elements.stars).toBeDefined(); 63 | }); 64 | 65 | it(`check number of stars items`, () => { 66 | expect(response.item.elements.stars.value.length).toEqual(2); 67 | }); 68 | 69 | it(`checks that linkedItemCodenames element is mapped and container proper data`, () => { 70 | expect(response.item.elements.plot.linkedItemCodenames).toBeDefined(); 71 | expect(response.item.elements.plot.linkedItemCodenames).toContain('tom_hardy'); 72 | expect(response.item.elements.plot.linkedItemCodenames).toContain('joel_edgerton'); 73 | }); 74 | 75 | it(`check that linked item (Actor) has 'first_name' text properly assigned`, () => { 76 | expect(response.item.elements.stars.linkedItems[0].elements.first_name.value).toEqual('Tom'); 77 | }); 78 | 79 | it(`images should be mapped in plot rich text element`, () => { 80 | const images = response.item.elements.plot.images; 81 | 82 | expect(images).toBeDefined(); 83 | expect(images.length).toEqual(2); 84 | }); 85 | 86 | it(`verify linked items included in response`, () => { 87 | expect(Object.keys(response.linkedItems).length).toEqual(3); 88 | for (const key of Object.keys(response.linkedItems)) { 89 | const linkedItem = response.linkedItems[key]; 90 | expect(linkedItem).toBeDefined(); 91 | } 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/browser/live-tests/items-feed/live-items-feed-all.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse, Contracts, Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Live items feed all', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | let response: Responses.IListItemsFeedAllResponse; 9 | const responses: IDeliveryNetworkResponse, Contracts.IItemsFeedContract>[] = []; 10 | 11 | beforeAll(async () => { 12 | response = ( 13 | await context.deliveryClient.itemsFeed().toAllPromise({ 14 | responseFetched: (innerResponse, nextPage, continuationToken) => { 15 | responses.push(innerResponse); 16 | } 17 | }) 18 | ).data; 19 | }); 20 | 21 | it(`items should be defined`, () => { 22 | expect(response).toBeDefined(); 23 | }); 24 | 25 | it(`items should have multiple responses`, () => { 26 | expect(response.responses.length).toBeGreaterThan(0); 27 | expect(response.responses.length).toEqual(responses.length); 28 | 29 | for (const innerResponse of response.responses) { 30 | expect(innerResponse.data).toBeDefined(); 31 | } 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/browser/live-tests/items-feed/live-items-feed.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse, Contracts, Responses } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Live items feed', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | let response: IDeliveryNetworkResponse; 9 | 10 | beforeAll(async () => { 11 | response = await context.deliveryClient.itemsFeed().toPromise(); 12 | }); 13 | 14 | it(`Response should have all properties assigned`, () => { 15 | expect(response.data.items).toEqual(jasmine.any(Array)); 16 | expect(response.data.items.length).toBeGreaterThan(0); 17 | expect(response.xContinuationToken).toBeUndefined(); 18 | expect(Object.keys(response.data.linkedItems).length).toBeGreaterThan(0); 19 | 20 | for (const item of response.data.items) { 21 | expect(item.system).toBeDefined(); 22 | expect(item.elements).toBeDefined(); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/browser/live-tests/items/live-items-all.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse, Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Live items all', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const type: string = 'movie'; 9 | let response: Responses.IListContentItemsAllResponse; 10 | const responses: IDeliveryNetworkResponse, any>[] = []; 11 | 12 | const pages: number = 2; 13 | const limit: number = 2; 14 | 15 | beforeAll(async () => { 16 | response = ( 17 | await context.deliveryClient 18 | .items() 19 | .limitParameter(limit) 20 | .type(type) 21 | .toAllPromise({ 22 | responseFetched: (innerResponse, nextPage, continuationToken) => { 23 | responses.push(innerResponse); 24 | }, 25 | pages: pages 26 | }) 27 | ).data; 28 | }); 29 | 30 | it(`items should be defined`, () => { 31 | expect(response).toBeDefined(); 32 | }); 33 | 34 | it(`check correct number of items`, () => { 35 | expect(response.items.length).toEqual(pages * limit); 36 | }); 37 | 38 | it(`items should have multiple responses`, () => { 39 | expect(response.responses.length).toEqual(pages); 40 | expect(response.responses.length).toEqual(responses.length); 41 | 42 | for (const innerResponse of response.responses) { 43 | expect(innerResponse.data).toBeDefined(); 44 | } 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/browser/live-tests/items/live-items.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Live items', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const type: string = 'movie'; 9 | let response: Responses.IListContentItemsResponse; 10 | 11 | beforeAll(async () => { 12 | response = (await context.deliveryClient.items().type(type).toPromise()).data; 13 | }); 14 | 15 | it(`items should be defined`, () => { 16 | expect(response).toBeDefined(); 17 | }); 18 | 19 | it(`check correct number of items`, () => { 20 | expect(response.items.length).toEqual(6); 21 | }); 22 | 23 | it(`items should have pagination`, () => { 24 | expect(response.pagination).toBeDefined(); 25 | }); 26 | 27 | it(`Linked items should be set`, () => { 28 | expect(Object.keys(response.linkedItems).length).toBeGreaterThan(0); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/browser/live-tests/localization/localization-default-language.spec.ts: -------------------------------------------------------------------------------- 1 | import { Context, Movie, setup } from '../../setup'; 2 | 3 | describe('Language #1', () => { 4 | const language = 'en'; 5 | const context = new Context(); 6 | context.defaultLanguage = language; 7 | setup(context); 8 | 9 | const movieCodename: string = 'warrior'; 10 | 11 | const query = context.deliveryClient.item(movieCodename).languageParameter(language); 12 | const queryLanguageParam = query.getParameters().find((m) => m.getParam() === `language=${language}`); 13 | 14 | it(`Language should be '${language}'`, () => { 15 | expect(queryLanguageParam).toBeDefined(); 16 | }); 17 | }); 18 | 19 | describe('Language #2', () => { 20 | const language = 'cz'; 21 | const context = new Context(); 22 | context.defaultLanguage = language; 23 | setup(context); 24 | 25 | const newMovieCodename: string = 'warrior'; 26 | 27 | const query = context.deliveryClient.item(newMovieCodename).languageParameter(language); 28 | const queryLanguageParam = query.getParameters().find((m) => m.getParam() === `language=${language}`); 29 | 30 | it(`language should be '${language}'`, () => { 31 | expect(queryLanguageParam).toBeDefined(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/browser/live-tests/localization/localized-item.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Localized item', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const language: string = 'cz'; 9 | const movieCodename: string = 'warrior'; 10 | let response: Responses.IViewContentItemResponse; 11 | 12 | beforeAll(async () => { 13 | response = (await context.deliveryClient.item(movieCodename).languageParameter(language).toPromise()) 14 | .data; 15 | }); 16 | 17 | it(`language should be '${language}'`, () => { 18 | expect(response.item.system.language).toEqual(language); 19 | }); 20 | 21 | it(`title should be localized`, () => { 22 | expect(response.item.elements.title.value).toEqual('Warrior-cz'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/browser/live-tests/mapping/live-mapping.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse, Contracts, Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Live mapping service', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const movieCodename: string = 'warrior'; 9 | let response: IDeliveryNetworkResponse, Contracts.IViewContentItemContract>; 10 | 11 | beforeAll(async () => { 12 | response = await context.deliveryClient.item(movieCodename).toPromise(); 13 | }); 14 | 15 | it(`raw response should be available for mapping from stored json (with mapping service)`, () => { 16 | const contractJson = response.response.data; 17 | const reMappedResponse = context.deliveryClient.mappingService.viewContentItemResponse(contractJson); 18 | expect(reMappedResponse).toBeDefined(); 19 | expect(reMappedResponse.item.system.codename).toEqual(movieCodename); 20 | expect(reMappedResponse.item.elements.title.value).toEqual('Warrior'); 21 | }); 22 | 23 | it(`raw response should be available for mapping from stored json (with query)`, () => { 24 | const contractJson = response.response.data; 25 | const reMappedResponse = context.deliveryClient.item(movieCodename).map(contractJson); 26 | expect(reMappedResponse).toBeDefined(); 27 | expect(reMappedResponse.item.system.codename).toEqual(movieCodename); 28 | expect(reMappedResponse.item.elements.title.value).toEqual('Warrior'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/browser/live-tests/network-response/network-response.spec.ts: -------------------------------------------------------------------------------- 1 | import { IDeliveryNetworkResponse } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Network response', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | let response: IDeliveryNetworkResponse; 9 | 10 | beforeAll(async () => { 11 | response = await context.deliveryClient 12 | .itemsFeed() 13 | .toPromise(); 14 | }); 15 | 16 | it(`response should contain network response data`, () => { 17 | expect(response.response.headers).toBeDefined(); 18 | expect(response.response.data).toBeDefined(); 19 | expect(response.response.rawResponse).toBeDefined(); 20 | expect(response.response.retryStrategy).toBeDefined(); 21 | expect(response.hasStaleContent).toEqual(false); 22 | expect(response.xContinuationToken).toBeUndefined(); 23 | expect(response.response.status).toEqual(200); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/browser/live-tests/pagination/pagination.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Pagination', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const type: string = 'movie'; 9 | let response: Responses.IListContentItemsResponse; 10 | 11 | beforeAll(async () => { 12 | response = ( 13 | await context.deliveryClient.items().type(type).limitParameter(5).skipParameter(1).toPromise() 14 | ).data; 15 | }); 16 | 17 | it(`pagination should be defined`, () => { 18 | expect(response.pagination).toBeDefined(); 19 | }); 20 | 21 | it(`check count`, () => { 22 | expect(response.pagination.count).toEqual(5); 23 | }); 24 | 25 | it(`check limit`, () => { 26 | expect(response.pagination.limit).toEqual(5); 27 | }); 28 | 29 | it(`check skip`, () => { 30 | expect(response.pagination.skip).toEqual(1); 31 | }); 32 | 33 | it(`check pagination count`, () => { 34 | expect(response.pagination.nextPage).toEqual(''); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/browser/live-tests/promise/response-by-promise.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, Movie, setup } from '../../setup'; 3 | 4 | describe('Response with Promises', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | let response: Responses.IListContentItemsResponse; 9 | 10 | beforeAll((done) => { 11 | context.deliveryClient 12 | .items() 13 | .toPromise() 14 | .then((xResponse) => { 15 | response = xResponse.data; 16 | done(); 17 | }) 18 | .catch((err) => { 19 | throw err; 20 | }); 21 | }); 22 | 23 | it(`Response items should be retrieved and mapped just like with Promise`, () => { 24 | expect(response).toBeDefined(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/browser/live-tests/readme.md: -------------------------------------------------------------------------------- 1 | # Live tests info 2 | 3 | Tests in this section depend on `live` environment 4 | -------------------------------------------------------------------------------- /test/browser/live-tests/taxonomy/live-taxonomies.spec.ts: -------------------------------------------------------------------------------- 1 | import { ITaxonomyGroup, Responses, ITaxonomyTerms } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Live taxonomies', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const termsWithNestedTermsCodename: string = 'film'; // codename of the taxonomy term that has nested terms 9 | const numberOfNestedTerms: number = 3; // this is the number of nested terms defined by 'termsWithNestedTermsCodename' 10 | const existingTaxonomyCodename: string = 'movietype'; // codename of some of the defined taxonomies 11 | 12 | let response: Responses.IListTaxonomiesResponse; 13 | let taxonomy: ITaxonomyGroup; 14 | 15 | beforeAll(async () => { 16 | response = (await context.deliveryClient.taxonomies().toPromise()).data; 17 | 18 | taxonomy = response.items.find((m) => m.system.codename === existingTaxonomyCodename) as ITaxonomyGroup; 19 | }); 20 | 21 | it(`taxonomies should have pagination`, () => { 22 | expect(response.pagination).toBeDefined(); 23 | }); 24 | 25 | it(`taxonomies should be defined`, () => { 26 | expect(response.items).toBeDefined(); 27 | }); 28 | 29 | it(`taxonomy with codename '${existingTaxonomyCodename}' should be defined`, () => { 30 | expect(taxonomy).toBeDefined(); 31 | }); 32 | 33 | it(`taxomy system attributes should be defined`, () => { 34 | if (!taxonomy) { 35 | throw Error('undefined'); 36 | } 37 | expect(taxonomy.system).toBeDefined(); 38 | expect(taxonomy.system.codename).toBeDefined(); 39 | expect(taxonomy.system.id).toBeDefined(); 40 | expect(taxonomy.system.lastModified).toBeDefined(); 41 | expect(taxonomy.system.name).toBeDefined(); 42 | }); 43 | 44 | it(`taxonomy group should match requested type`, () => { 45 | if (!taxonomy) { 46 | throw Error('undefined'); 47 | } 48 | expect(taxonomy.system.codename).toEqual(existingTaxonomyCodename); 49 | }); 50 | 51 | it(`taxonomy group should have defined terms`, () => { 52 | if (!taxonomy) { 53 | throw Error('undefined'); 54 | } 55 | expect(taxonomy.terms).toBeDefined(); 56 | }); 57 | 58 | it(`taxonomy group should have > 0 terms`, () => { 59 | if (!taxonomy) { 60 | throw Error('undefined'); 61 | } 62 | expect(taxonomy.terms.length).toBeGreaterThan(0); 63 | }); 64 | 65 | it(`taxonomy group should contain nested taxonomies`, () => { 66 | const termsWithNestedTerms = taxonomy.terms.find( 67 | (m) => m.codename === termsWithNestedTermsCodename 68 | ) as ITaxonomyTerms; 69 | expect(termsWithNestedTerms).toBeDefined(); 70 | 71 | expect(termsWithNestedTerms.terms).toBeDefined(); 72 | expect(termsWithNestedTerms.terms.length).toEqual(numberOfNestedTerms); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/browser/live-tests/taxonomy/live-taxonomy.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses, ITaxonomyTerms } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Live taxonomy', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const taxonomyCodename: string = 'movietype'; 9 | const termsWithNestedTermsCodename: string = 'film'; // codename of the taxonomy term that has nested terms 10 | const numberOfNestedTerms: number = 3; // this is the number of nested terms defined by 'termsWithNestedTermsCodename' 11 | 12 | let response: Responses.IViewTaxonomyResponse; 13 | 14 | beforeAll(async () => { 15 | response = (await context.deliveryClient.taxonomy(taxonomyCodename).toPromise()).data; 16 | }); 17 | 18 | it(`taxomy should be defined`, () => { 19 | expect(response.taxonomy).toBeDefined(); 20 | }); 21 | 22 | it(`taxomy system attributes should be defined`, () => { 23 | expect(response.taxonomy.system).toBeDefined(); 24 | expect(response.taxonomy.system.codename).toBeDefined(); 25 | expect(response.taxonomy.system.id).toBeDefined(); 26 | expect(response.taxonomy.system.lastModified).toBeDefined(); 27 | expect(response.taxonomy.system.name).toBeDefined(); 28 | }); 29 | 30 | it(`taxonomy group should match requested type`, () => { 31 | expect(response.taxonomy.system.codename).toEqual(taxonomyCodename); 32 | }); 33 | 34 | it(`taxonomy group should have defined terms`, () => { 35 | expect(response.taxonomy.terms).toBeDefined(); 36 | }); 37 | 38 | it(`taxonomy group should have > 0 terms`, () => { 39 | expect(response.taxonomy.terms.length).toBeGreaterThan(0); 40 | }); 41 | 42 | it(`taxonomy group should contain nested taxonomies`, () => { 43 | const termsWithNestedTerms = response.taxonomy.terms.find( 44 | (m) => m.codename === termsWithNestedTermsCodename 45 | ) as ITaxonomyTerms; 46 | expect(termsWithNestedTerms).toBeDefined(); 47 | expect(termsWithNestedTerms.terms).toBeDefined(); 48 | expect(termsWithNestedTerms.terms.length).toEqual(numberOfNestedTerms); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/browser/live-tests/types/live-type.spec.ts: -------------------------------------------------------------------------------- 1 | import { IGenericElement, Responses } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Live type', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | const codename: string = 'movie'; 9 | let response: Responses.IViewContentTypeResponse; 10 | 11 | const multipleChoiceElement: string = 'category'; 12 | const taxonomyElement: string = 'releasecategory'; 13 | 14 | beforeAll(async () => { 15 | response = (await context.deliveryClient.type(codename).toPromise()).data; 16 | }); 17 | 18 | it(`type should be defined`, () => { 19 | expect(response).toBeDefined(); 20 | }); 21 | 22 | it(`elements should be defined`, () => { 23 | expect(response.type.elements).toBeDefined(); 24 | }); 25 | 26 | it(`system properties should be defined`, () => { 27 | expect(response.type.system).toBeDefined(); 28 | }); 29 | 30 | it(`proper type should be returned based on test config`, () => { 31 | expect(response.type.system.codename).toEqual(codename); 32 | }); 33 | 34 | it(`Verifies taxonomy element - '${taxonomyElement}'`, () => { 35 | const releasecategoryElement = response.type.elements.find( 36 | (m) => m.codename === taxonomyElement 37 | ) as IGenericElement; 38 | expect(releasecategoryElement).toBeDefined(); 39 | expect(releasecategoryElement.taxonomyGroup).toBeDefined(); 40 | expect(releasecategoryElement.taxonomyGroup).toEqual('releasecategory'); 41 | }); 42 | 43 | it(`Verifies multiple_choice element - '${multipleChoiceElement}'`, () => { 44 | const categoryElement = response.type.elements.find( 45 | (m) => m.codename === multipleChoiceElement 46 | ) as IGenericElement; 47 | 48 | expect(categoryElement).toBeDefined(); 49 | expect(categoryElement.options).toBeDefined(); 50 | expect(categoryElement.options.length).toBeGreaterThan(0); 51 | expect(categoryElement.options[0].codename).toBeDefined(); 52 | expect(categoryElement.options[0].name).toBeDefined(); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/browser/live-tests/types/live-types.spec.ts: -------------------------------------------------------------------------------- 1 | import { Responses } from '../../../../lib'; 2 | import { Context, setup } from '../../setup'; 3 | 4 | describe('Live types', () => { 5 | const context = new Context(); 6 | setup(context); 7 | 8 | let response: Responses.IListContentTypesResponse; 9 | 10 | beforeAll(async () => { 11 | response = (await context.deliveryClient.types().toPromise()).data; 12 | }); 13 | 14 | it(`types should be defined`, () => { 15 | expect(response).toBeDefined(); 16 | }); 17 | 18 | it(`types should have pagination`, () => { 19 | expect(response.pagination).toBeDefined(); 20 | }); 21 | 22 | it(`there should be at least 1 type`, () => { 23 | expect(response.items.length).toBeGreaterThan(0); 24 | }); 25 | 26 | it(`elements should be defined`, () => { 27 | expect(response.items[0].elements).toBeDefined(); 28 | }); 29 | 30 | it(`system properties should be defined`, () => { 31 | expect(response.items[0].system).toBeDefined(); 32 | }); 33 | 34 | it(`pagination should be defined`, () => { 35 | expect(response.pagination).toBeDefined(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/browser/setup/context.ts: -------------------------------------------------------------------------------- 1 | import { IHeader, IRetryStrategyOptions } from '@kontent-ai/core-sdk'; 2 | import { DeliveryClient, IDeliveryClientConfig, IQueryConfig } from '../../../lib'; 3 | 4 | export class Context { 5 | public deliveryClient!: DeliveryClient; 6 | 7 | public defaultQueryConfig?: IQueryConfig; 8 | public environmentId!: string; 9 | public previewApiKey?: string; 10 | public securedApiKey?: string; 11 | public usePreviewMode: boolean = false; 12 | public useSecuredMode: boolean = false; 13 | public defaultLanguage?: string; 14 | public excludeArchivedItems?: boolean = false; 15 | public baseUrl?: string; 16 | public basePreviewUrl?: string; 17 | public globalHeaders?: (queryConfig: IQueryConfig) => IHeader[]; 18 | public retryStrategy?: IRetryStrategyOptions; 19 | 20 | constructor(options?: { 21 | defaultQueryConfig?: IQueryConfig; 22 | environmentId?: string; 23 | previewApiKey?: string; 24 | deliveryClient?: DeliveryClient; 25 | defaultLanguage?: string; 26 | excludeArchivedItems?: boolean; 27 | baseUrl?: string; 28 | basePreviewUrl?: string; 29 | securedApiKey?: string; 30 | globalHeaders?: (queryConfig: IQueryConfig) => IHeader[]; 31 | retryStrategy?: IRetryStrategyOptions; 32 | }) { 33 | if (options) { 34 | Object.assign(this, options); 35 | } 36 | } 37 | 38 | getConfig(): IDeliveryClientConfig { 39 | return { 40 | environmentId: this.environmentId, 41 | previewApiKey: this.previewApiKey, 42 | secureApiKey: this.securedApiKey, 43 | defaultQueryConfig: this.defaultQueryConfig, 44 | proxy: { 45 | baseUrl: this.baseUrl, 46 | basePreviewUrl: this.basePreviewUrl 47 | }, 48 | defaultLanguage: this.defaultLanguage, 49 | globalHeaders: this.globalHeaders, 50 | retryStrategy: this.retryStrategy, 51 | excludeArchivedItems: this.excludeArchivedItems 52 | }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/browser/setup/delivery-test-client.ts: -------------------------------------------------------------------------------- 1 | import { IHeader, IResponse, TestHttpService } from '@kontent-ai/core-sdk'; 2 | 3 | import { createDeliveryClient, IDeliveryClient, IDeliveryClientConfig } from '../../../lib'; 4 | 5 | const testenvironmentId: string = 'delivery-environment-id'; 6 | 7 | export function toPromise(data: T): Promise { 8 | return new Promise((resolve, reject) => { 9 | resolve(data); 10 | }); 11 | } 12 | 13 | export function getTestDeliveryClient(config?: IDeliveryClientConfig): IDeliveryClient { 14 | return createDeliveryClient( 15 | config 16 | ? config 17 | : { 18 | environmentId: testenvironmentId 19 | } 20 | ); 21 | } 22 | 23 | export function getDeliveryClientWithError(errorJson: any): IDeliveryClient { 24 | return createDeliveryClient({ 25 | environmentId: testenvironmentId, 26 | httpService: new TestHttpService({ 27 | response: undefined, 28 | error: errorJson 29 | }) 30 | }); 31 | } 32 | 33 | export function getDeliveryClientWithJson( 34 | json: any, 35 | config?: IDeliveryClientConfig, 36 | responseHeaders: IHeader[] = [] 37 | ): IDeliveryClient { 38 | return getDeliveryClientWithJsonAndHeaders(json, config, responseHeaders); 39 | } 40 | 41 | export function getDeliveryClientWithJsonAndHeaders( 42 | json: any, 43 | config?: IDeliveryClientConfig, 44 | responseHeaders: IHeader[] = [] 45 | ): IDeliveryClient { 46 | if (!config) { 47 | return createDeliveryClient({ 48 | environmentId: testenvironmentId, 49 | httpService: new TestHttpService({ 50 | response: getResponseFromJson(json, responseHeaders), 51 | error: undefined 52 | }) 53 | }); 54 | } 55 | 56 | // always set http service 57 | config.httpService = new TestHttpService({ 58 | response: getResponseFromJson(json, responseHeaders), 59 | error: undefined 60 | }); 61 | 62 | return createDeliveryClient(config); 63 | } 64 | 65 | function getResponseFromJson(json: any, responseHeaders: IHeader[] = []): IResponse { 66 | return { 67 | data: json, 68 | headers: responseHeaders, 69 | rawResponse: json, 70 | status: 999, 71 | retryStrategy: { 72 | retryAttempts: 1, 73 | options: {} 74 | } 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /test/browser/setup/index.ts: -------------------------------------------------------------------------------- 1 | export * from './models'; 2 | export * from './context'; 3 | export * from './setup'; 4 | export * from './delivery-test-client'; 5 | -------------------------------------------------------------------------------- /test/browser/setup/models.ts: -------------------------------------------------------------------------------- 1 | import { Elements, IContentItem } from '../../../lib'; 2 | 3 | type LanguageCodenames = 'default' | 'en'; 4 | type CollectionCodenames = 'default'; 5 | type WorkflowCodenames = 'default'; 6 | type WorkflowStepCodenames = 'draft' | 'published' | 'review'; 7 | type ActorElementCodenames = 'first_name' | 'last_name' | 'photo'; 8 | 9 | export type Actor = IContentItem< 10 | { 11 | readonly first_name: Elements.TextElement; 12 | readonly last_name: Elements.TextElement; 13 | readonly photo: Elements.AssetsElement; 14 | }, 15 | 'actor', 16 | LanguageCodenames, 17 | CollectionCodenames, 18 | WorkflowCodenames, 19 | WorkflowStepCodenames, 20 | ActorElementCodenames 21 | >; 22 | 23 | export type ReleaseCategoryTaxonomy = 'global_release' | 'us_only' | 'local_release'; 24 | export type CategoryMultipleChoiceOptionCodenames = 'sci_fi' | 'action' | 'comedy' | 'drama' | 'romance' | 'animation'; 25 | 26 | type MovieElementCodenames = 27 | | 'title' 28 | | 'plot' 29 | | 'released' 30 | | 'length' 31 | | 'poster' 32 | | 'category' 33 | | 'stars' 34 | | 'seoname' 35 | | 'releasecategory'; 36 | 37 | export type Movie = IContentItem< 38 | { 39 | readonly title: Elements.TextElement; 40 | readonly plot: Elements.RichTextElement; 41 | readonly released: Elements.DateTimeElement; 42 | readonly length: Elements.NumberElement; 43 | readonly category: Elements.MultipleChoiceElement; 44 | readonly seoname: Elements.UrlSlugElement; 45 | readonly poster: Elements.AssetsElement; 46 | readonly stars: Elements.LinkedItemsElement; 47 | readonly releasecategory: Elements.TaxonomyElement; 48 | }, 49 | 'movie', 50 | LanguageCodenames, 51 | CollectionCodenames, 52 | WorkflowCodenames, 53 | WorkflowStepCodenames, 54 | MovieElementCodenames 55 | >; 56 | -------------------------------------------------------------------------------- /test/browser/setup/setup.ts: -------------------------------------------------------------------------------- 1 | import { DeliveryClient, IDeliveryClientConfig } from '../../../lib'; 2 | import { Context } from './context'; 3 | 4 | const defaultenvironmentId = 'da5abe9f-fdad-4168-97cd-b3464be2ccb9'; 5 | 6 | // tslint:disable-next-line:max-line-length 7 | const defaultPreviewApiKey = 8 | 'ew0KICAiYWxnIjogIkhTMjU2IiwNCiAgInR5cCI6ICJKV1QiDQp9.ew0KICAidWlkIjogInVzcl8wdlFZQkNxQXZybm81cmlmSG5pWUVHIiwNCiAgImVtYWlsIjogInJpY2hhcmRzQGtlbnRpY28uY29tIiwNCiAgInByb2plY3RfaWQiOiAiZGE1YWJlOWYtZmRhZC00MTY4LTk3Y2QtYjM0NjRiZTJjY2I5IiwNCiAgImp0aSI6ICJpLXNFVWJlNmZPeUtBQmJOIiwNCiAgInZlciI6ICIxLjAuMCIsDQogICJnaXZlbl9uYW1lIjogIlJpY2hhcmQiLA0KICAiZmFtaWx5X25hbWUiOiAiU3VzdGVrIiwNCiAgImF1ZCI6ICJwcmV2aWV3LmRlbGl2ZXIua2VudGljb2Nsb3VkLmNvbSINCn0.jSq0owesXGAGf8l7e0Ue7wPkP28MT_--ZK5T02sO7yw'; 9 | 10 | const defaultSecuredApiKey = 'securedApiKey'; 11 | 12 | // ----------- setup function o------------ // 13 | export function setup(context: Context) { 14 | // get delivery client with given context 15 | let environmentId: string = defaultenvironmentId; 16 | let previewApiKey: string = defaultPreviewApiKey; 17 | let securedApiKey: string = defaultSecuredApiKey; 18 | 19 | // context settings override default setup 20 | if (context.environmentId) { 21 | environmentId = context.environmentId; 22 | } 23 | 24 | if (context.previewApiKey) { 25 | previewApiKey = context.previewApiKey; 26 | } 27 | 28 | if (context.securedApiKey) { 29 | securedApiKey = context.securedApiKey; 30 | } 31 | 32 | const deliveryClientConfig: IDeliveryClientConfig = { 33 | environmentId: environmentId, 34 | secureApiKey: securedApiKey, 35 | previewApiKey: previewApiKey, 36 | defaultQueryConfig: context.defaultQueryConfig, 37 | defaultLanguage: context.defaultLanguage, 38 | excludeArchivedItems: context.excludeArchivedItems, 39 | proxy: { 40 | baseUrl: context.baseUrl, 41 | basePreviewUrl: context.basePreviewUrl 42 | }, 43 | retryStrategy: context.retryStrategy, 44 | globalHeaders: context.globalHeaders 45 | }; 46 | 47 | // set context 48 | context.environmentId = environmentId; 49 | context.previewApiKey = previewApiKey; 50 | context.securedApiKey = securedApiKey; 51 | 52 | // set delivery client 53 | context.deliveryClient = new DeliveryClient(deliveryClientConfig); 54 | } 55 | -------------------------------------------------------------------------------- /test/node/base/base-node-test.js: -------------------------------------------------------------------------------- 1 | const KontentDelivery = require('../../../dist/cjs'); 2 | const assert = require('assert'); 3 | const environmentId = 'da5abe9f-fdad-4168-97cd-b3464be2ccb9'; 4 | 5 | const deliveryClient = new KontentDelivery.DeliveryClient({ 6 | environmentId: environmentId, 7 | }); 8 | 9 | describe('Base node.js test', () => { 10 | 11 | let response; 12 | 13 | before(async () => { 14 | response = await deliveryClient.items() 15 | .type('movie') 16 | .limitParameter(10) 17 | .toPromise(); 18 | }); 19 | 20 | it('Response should be successful and should contain an item of defined type', () => { 21 | var item = response.data.items[0]; 22 | 23 | assert.ok(item); 24 | assert.ok(item.system.codename); 25 | }); 26 | }); 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "outDir": "dist", 6 | "sourceMap": true, 7 | "declaration": true, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "module": "commonjs", 12 | "target": "es6", 13 | "strict": true, 14 | "strictNullChecks": true, 15 | "strictPropertyInitialization": true, 16 | "resolveJsonModule": true, 17 | "noImplicitReturns": true, 18 | "allowUnreachableCode": false, 19 | "alwaysStrict": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "noUnusedParameters": false, 22 | "noUnusedLocals": true, 23 | "strictFunctionTypes": true, 24 | "noImplicitAny": true, 25 | "typeRoots": ["./node_modules/@types"], 26 | "lib": ["ESNext", "dom"] 27 | }, 28 | "include": ["lib/**/*", "test/**/*", "misc/**/*", "clean.ts", "eslint.config.mjs"], 29 | "exclude": ["node_modules"] 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "lib/**/*" 5 | ] 6 | } -------------------------------------------------------------------------------- /tsconfig.webpack.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.prod.json", 3 | "compilerOptions": { 4 | "declaration": false 5 | } 6 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 3 | 4 | const isProd = (args) => { 5 | return args.mode === 'production' 6 | } 7 | 8 | const outputFolder = (args) => path.resolve(__dirname, 'dist/bundles'); 9 | const bundleFilename = (args) => { 10 | return 'kontent-delivery.umd' + (isProd(args) ? '.min.js' : '.js'); 11 | } 12 | 13 | module.exports = (env, args) => ({ 14 | mode: args.mode, 15 | entry: { 16 | 'index': './lib/index.ts', 17 | }, 18 | resolve: { 19 | extensions: ['.ts', '.js'] 20 | }, 21 | output: { 22 | path: outputFolder(args), 23 | filename: bundleFilename(args), 24 | libraryTarget: 'umd', 25 | umdNamedDefine: true, 26 | library: 'kontentDelivery' 27 | }, 28 | devtool: 'source-map', 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.ts$/, 33 | loader: 'ts-loader', 34 | include: [ 35 | path.resolve(__dirname, 'lib'), 36 | ], 37 | options: { 38 | configFile: require.resolve('./tsconfig.webpack.json') 39 | } 40 | } 41 | ], 42 | }, 43 | performance: { 44 | hints: 'warning', 45 | maxEntrypointSize: 1000000, 46 | maxAssetSize: 1000000 47 | }, 48 | plugins: [ 49 | new BundleAnalyzerPlugin({ 50 | generateStatsFile: true, 51 | analyzerMode: 'json', 52 | reportFilename: (isProd(args) ? 'report.min' : 'report') + '.json', 53 | statsFilename: (isProd(args) ? 'stats.min' : 'stats') + '.json' 54 | }) 55 | ] 56 | }); 57 | 58 | 59 | --------------------------------------------------------------------------------