Giter VIP home page Giter VIP logo

management-sdk-js's Introduction

Kontent.ai Management Javascript SDK

Javascript SDK for the Management API. Helps you manage content in your Kontent.ai projects. Supports both node.js and browsers.

npm version Build & Test npm Known Vulnerabilities GitHub license Gzip bundle

Getting started

To get started, you'll first need to have access to your Kontent.ai project where you need to enable Content management API and generate access token that will be used to authenticate all requests made by this library.

Installation

npm i @kontent-ai/management-sdk --save

Using a standalone version in browsers

If you'd like to use this library directly in browser, place following script tags to your html page. You may of course download it and refer to local versions of scripts.

<script src="https://cdn.jsdelivr.net/npm/@kontent-ai/management-sdk@latest/dist/bundles/kontent-management.umd.js"></script>

Making the first request

The following code example shows how to create new content item in your Kontent.ai environment.

import { createManagementClient } from '@kontent-ai/management-sdk';

const client = createManagementClient({
    environmentId: 'xxx', // id of your Kontent.ai environment
    subscriptionId: 'zzz' // optional, but required for Subscription related endpoints
    apiKey: 'yyy' // Content management API token
});

client
    .addContentItem()
    .withData({
        name: 'New article',
        type: {
            codename: 'article' // codename of content type
        }
    })
    .toPromise()
    .then(response => {
        console.log(response);
    });

If you are using UMD bundles directly in browsers, you can find this library under KontentManagement global variable.

<!DOCTYPE html>
<html>
    <head>
        <title>Kontent.ai management | jsdelivr cdn</title>
        <script src="https://cdn.jsdelivr.net/npm/@kontent-ai/management-sdk/dist/bundles/kontent-management.umd.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            var KontentManagement = window['kontentManagement'];

            var client = new KontentManagement.ManagementClient({
                environmentId: 'xxx',
                // using CM API key in browser is NOT safe. If you need to use SDK in browsers
                // you should use proxy server and set authorization header there rather than here
                apiKey: 'yyy',
                subscriptionId: 'zzz' // optional, but required for Subscription related endpoints
            });

            client
                .addContentItem()
                .withData({
                    name: 'New article',
                    type: {
                        codename: 'article'
                    }
                })
                .toPromise()
                .then(response => {
                    console.log(response);
                });
        </script>
    </body>
</html>

Configuration

The ManagementClient contains several configuration options:

import { createManagementClient } from '@kontent-ai/management-sdk';

const client = createManagementClient({
    // configuration options
});
Option Default Description
API Key N/A Required - Management or Subscription API Key. Subscription API Key also works for Management requests
environmentId N/A Required for Management API - Environment Id
subscriptionId N/A Required for Subscription API - Subscription Id
baseUrl https://manage.kontent.ai/v2 Base URL of REST api. Can be useful if you are using custom proxy or for testing purposes
retryStrategy undefined Retry strategy configuration. If not set, default strategy is used.
httpService HttpService Used to inject implementation of IHttpService used to make HTTP request across network. Can also be useful for testing purposes by returning specified responses.

Handling API Management Errors

See the error section in Management API reference for infofmation about status codes and error messages.

try {
    const client = createManagementClient({
        environmentId: 'x',
        apiKey: 'y'
    });
    await client.viewContentItem().byItemCodename('invalid codename').toPromise();
} catch (err) {
    if (err instanceof SharedModels.ContentManagementBaseKontentError) {
        // Error message provided by API response and mapped by SDK
        const message = err.message;
        // In case you need more specific information about the request error.
        // Structure of the error depends on HttpService.
        const error = error.originalError;
    } else {
        // handle generic error however you need
    }
}

Cancelling Requests

const client = {
    environmentId: 'x',
    apiKey: 'y'
});

// prepare cancel token
const cancelTokenRequest = client.createCancelToken();

client
    .listContentItems()
    .withCancelToken(cancelTokenRequest)
    .toPromise()
    .then((x) => {
        // will not be executed as request was cancelled before network
        // request got a chance to finish
    })
    .catch((err) => {
        // error with your custom message 'Request manually cancelled'
    });

// cancel request right away
cancelTokenRequest.cancel('Request manually cancelled');

Sample scenario

Following is a sample scenario consisting of:

  1. Initializing client
  2. Getting default language of environment
  3. Creating new taxonomy with terms
  4. Creating new content type
  5. Creating content item
  6. Creating binary file & asset from URL
  7. Upserting language variant of the newly created content item in default language

Initializing client

import { createManagementClient } from '@kontent-ai/management-sdk';

const client = createManagementClient({
    environmentId: 'x',
    apiKey: 'y'
});

Getting default language of environment

const languages = await client.listLanguages().toPromise();
const defaultLanguage = languages.data.items.find((m) => m.isDefault);

Creating new taxonomy with terms

const movieTaxonomy = await client
    .addTaxonomy()
    .withData({
        name: 'Genre',
        codename: 'genre',
        terms: [
            {
                name: 'Comedy',
                codename: 'comedy',
                terms: []
            },
            {
                name: 'Action',
                codename: 'action',
                terms: []
            },
            {
                name: 'Anime',
                codename: 'anime',
                terms: []
            }
        ]
    })
    .toPromise();

Creating new content type

const movieType = await client
    .addContentType()
    .withData((builder) => {
        return {
            name: 'Movie',
            codename: 'movie',
            elements: [
                builder.textElement({
                    name: 'Title',
                    codename: 'title',
                    type: 'text',
                    is_required: true,
                    maximum_text_length: {
                        applies_to: 'characters',
                        value: 50
                    }
                }),
                builder.dateTimeElement({
                    name: 'ReleaseDate',
                    codename: 'release_date',
                    type: 'date_time',
                    is_required: true
                }),
                builder.richTextElement({
                    name: 'Description',
                    codename: 'description',
                    type: 'rich_text',
                    is_required: true,
                    allowed_table_formatting: ['unstyled', 'bold', 'italic'],
                    allowed_blocks: ['text', 'tables'],
                    maximum_text_length: {
                        applies_to: 'words',
                        value: 500
                    }
                }),
                builder.assetElement({
                    name: 'Cover',
                    codename: 'cover',
                    type: 'asset',
                    allowed_file_types: 'adjustable',
                    asset_count_limit: {
                        condition: 'exactly',
                        value: 1
                    },
                    is_required: true
                }),
                builder.taxonomyElement({
                    type: 'taxonomy',
                    codename: 'genre',
                    is_required: true,
                    taxonomy_group: {
                        id: movieTaxonomy.data.id
                    }
                })
            ]
        };
    })
    .toPromise();

Creating content item

const contentItem = await client
    .addContentItem()
    .withData({
        name: 'Warrior',
        type: {
            codename: 'movie'
        }
    })
    .toPromise();

Creating binary file & asset from URL

const imageAsset = await client
    .uploadAssetFromUrl()
    .withData({
        asset: {
            descriptions: [{ description: 'Image poster for warrior', language: { id: defaultLanguage?.id ?? '' } }],
            title: 'Warrior cover'
        },
        binaryFile: {
            filename: 'warrior.jpg'
        },
        fileUrl: 'https://upload.wikimedia.org/wikipedia/en/e/e3/Warrior_Poster.jpg'
    })
    .toPromise();

Upserting language variant of the newly created content item in default language

const languageVariant = await client
    .upsertLanguageVariant()
    .byItemId(contentItem.data.id)
    .byLanguageId(defaultLanguage?.id ?? '')
    .withData((builder) => {
        return {
            elements: [
                builder.textElement({
                    element: {
                        codename: 'title'
                    },
                    value: 'Warrior'
                }),
                builder.dateTimeElement({
                    element: {
                        codename: 'release_date'
                    },
                    value: '2011-09-09'
                }),
                builder.richTextElement({
                    element: {
                        codename: 'description'
                    },
                    value: '<p>Path that puts the fighter on a collision course with his estranged, older brother.</p>'
                }),
                builder.assetElement({
                    element: {
                        codename: 'cover'
                    },
                    value: [
                        {
                            id: imageAsset.data.id
                        }
                    ]
                }),
                builder.taxonomyElement({
                    element: {
                        codename: 'genre'
                    },
                    value: [
                        {
                            codename: 'action'
                        }
                    ]
                })
            ]
        }
    })
    .toPromise();

Testing

If you want to mock http responses, it is possible to use external implementation of configurable Http Service as a part of the client configuration.

Troubleshooting & feedback

If you have any issues or want to share your feedback, please feel free to create an issue in this GitHub repository.

Contributions

Contributions are welcomed. If you have an idea of what you would like to implement, let us know and lets discuss details of your PR.

management-sdk-js's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

management-sdk-js's Issues

Add support for non-localizable elements in content types/snippets

Motivation

We have added a non-localizable flag to content type elements and content type snippet elements. When this flag is set to true, the element is only editable in a default language variant and other language variants inherit the value from the default language variant.

โš ๏ธ This feature will be released (early access) in the middle of March.

Proposed solution

The SDK should support new functionality:

  • create content type/snippet with non-localizable elements
    • only text, datetime, multiple choice and number elements are supported for now , but all elements have the is_non_localizable property
  • add non-localizable elements to a content type/snippet
  • is_non_localizable can only be set during element creation, so there's no need to support update operation

Unable to call UpsertContentItem with Type: string property

Brief bug description

The upsertContentItem returns BadRequest when the type property is sent as string.

Repro steps

    const contentItemResult = client
        .upsertContentItem()
        .byItemExternalId("externalId2")
        .withData({
            name: "Hello World",
            type: "type2",
            collection: { codename: "second_collection" },
        })
        .toObservable()
        .subscribe((response) => {
            console.log(response);
          },
            (error) => {
              console.log(error);
            });

Run this code and manageApi returns BadRequest with message:
"The reference in 'type' must be an object containing exactly one identifier - id, codename, or external_id. See https://docs.kontent.ai/mapi-v2-reference for details."

Expected behavior

The upsert operation should be successful

Additional context

I believe that type property should have been typed as IReferenceObjectContract
https://github.com/Kentico/kontent-management-sdk-js/blob/914e022a58adb2c7b4e68980041087c75b459235/lib/contracts/content-item-contracts.ts#L45

Add support for Allowed link types

Motivation

Allowed link types in RichText element are soon to be added to Kontent, extending the RichText model with a allowed_item_link_types property. This change should be reflected in the SDK.

Proposed solution

Add the new property allowed_item_link_types to IRichTextElementData (and perhaps somewhere else too)

Add support for multiple workflows

Motivation

We have added multiple workflows to MAPI and slightly adjusted even some existing endpoints to reflect the new feature.

Proposed solution

  • Deprecate change workflow step action, endpoint: PUT /projects/{project_id}/items/{item_identifier}/variants/{language_identifier}/workflow/{workflow_step_identifier}

  • Workflow response model is common for all endpoints that return the workflow entity. It is a little bit different from the insert request model.

{
   id, // UUID string (internal ID)
   name, // string
   codename, // string
   steps: [
     {
       id, // UUID string (internal ID)
       name, // string
       codename, // string
       color, // string, possible/accepted values: https://kentico.atlassian.net/browse/CTC-1565?focusedCommentId=481083
       transitions_to: [
         {
            step, // ManageApiReferenceModel
         }
       ],
       role_ids, // array of UUID strings (internal IDs)
     }
   ],
   scopes: [
     {
       content_types, // array of ManageApiReferenceModel (can be an internal ID or codename)
     }
   ],
   published_step: {
     id, // UUID string (internal ID)
     name, // string
     codename, // string
     create_new_version_role_ids, // array of UUID strings (internal IDs)
     unpublish_role_ids, // array of UUID strings (internal IDs)
   },
   archived_step: {
     id, // UUID string (internal ID)
     name, // string
     codename, // string
     role_ids, // array of UUID strings (internal IDs)
   }
 } 
  • Get all workflows
    • add endpoint: GET /projects/{project_id}/workflows
    • returns an array of workflows
  • Insert workflow
    • add endpoint: POST /projects/{project_id}/workflows
    • returns the inserted workflow
    • request model
{
  name, // string
  codename, // string - optional
  steps: [
    {
      name, // string
      codename, // string
      color, // string, possible/accepted values: https://kentico.atlassian.net/browse/CTC-1565?focusedCommentId=481083
      transitions_to: [ // cannot be empty
        { 
          step, // ManageApiReferenceModel
        }
      ],
      role_ids, // array of UUID strings (cannot be empty)
    }
  ],
  scopes: [ (0 or 1 items)
    {
      content_types, // array of ManageApiReferenceModel
    }
  ],
  published_step: {
    create_new_version_role_ids, // array of UUID strings (cannot be empty)
    unpublish_role_ids, // array of UUID strings (cannot be empty)
  },
  archived_step: {
    role_ids, // array of UUID strings (cannot be empty)
  }
}
  • Update workflow
    • add endpoint: PUT /projects/{project_id}/workflows/<workflow_identifier>
    • request model is the same as for Insert
    • returns the updated workflow
  • Delete workflow
    • add endpoint : DELETE /projects/{project_id}/worfklows/<workflow_identifier>
    • does not return anything
  • Change workflow + step of variant directly
    • add endpoint: PUT /projects/{project_id}>/items/<item_identifier>}/variants/<language_identifier>/change-workflow
    • does not return anything
    • The request model contains only the required workflow property of the same type as workflow in variant upsert. See model below.
  • Change workflow + step via variant upsert
    • adjust endpoints: PUT /projects/{project_id}/items/<item_identifier>/variants/<language_identifier>
    • extend the request model with an optional workflow property. The internal properties are both required.
    • extend the content item variant response model (returned from all endpoints) with the workflow property. The shape is the same as the one from the request model but it will always be present. It duplicates the information of the workflow_step property so that one can be made deprecated.
elements: [], // Already present
workflow: {
 workflow_identifier, // ManageApiReferenceModel, workflow internal id or codename
 step_identifier // ManageApiReferenceModel, workflow step internal id or codename
}

Additional context

We do not support external ids in the ManageApiReferenceModel for workflows and workflow steps. We simply ignore them.
This feature will be released at the end of March.

Add support for Asset Renditions and their referencing

Motivation

Asset Renditions were added to Kontent App a while ago. They can be referenced from the Asset element in order to customize images. Now the feature has made its way to MAPI as well, and we should add support for managing renditions and referencing them to SDK as well.

Proposed solution

  • Models and endpoints for CRUD operations for Asset rendition entity (/asset/{assetId}/renditions/{renditionId})
  • Error handling
  • Extending the current value of AssetElement with renditions property (ReadonlyArray<id || externalId>. To give you an idea, this is the Before and After state of the Asset element in v2/variant endpoint:

BEFORE

{
  element: {
    codename: 'cover'
  },
  value: [
    {
      id: imageAsset.data.id
    }
  ]
}

AFTER

{
  element: {
    codename: 'cover'
  },
  value: [
    {
      id: imageAsset.data.id,
      "renditions": [{
        "id": '543ef1c7-a8ba-4bb4-b1b3-7fdbeb2fbebd'
      }]
    }
  ]
}

Additional context

The feature will be publically released in MAPI ~7.3.2022.
Documentation is not yet public.

Add regex validation data to URL slug element model

Motivation

Regex validations for URL slug elements are to be added to Kontent. This extends models of URL slug elements with a regex object. This addition should be reflected in the SDK.

Proposed solution

Extend URL slug model with the same regex object as is used in text elements. The changes should be the same as done it those two PRs that modified text element in the same way:
#54
#56

Additional context

The feature is planned to be released on March 2. Documentation is not public yet, but you can take inspiration from documentation of regex validations for text elements as the regex object is the same: https://kontent.ai/learn/reference/management-api-v2/#section/Text-element-in-type

Edit: bad link

Inconsistent encoding of content ids

Brief bug description

My id's have a # in the external-id (emanating from the recommended SingleTable design pattern for AWS DynamoDB). These content items are now unobtainable using the management API

Repro steps

  1. Create a content item with an external id with a # the value using Rest.
curl --location --request POST 'https://{{$project_id}}/items' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{$api_token}}' \
--data-raw '{
    "name": "deleteMe",
    "type": {
        "codename": "tag"
    },
    "external_id": "TAG#A"
}'
  1. Try to access the ContentItemVariant using the sdk.
const externalId = 'TYPE#A'
const safeId = externalId.split('#').join('%23')
const response = await client.viewLanguageVariant().byItemExternalId(safeId).byLanguageId(consts.KONTENT_DEFAULT_LANGUAGE).toPromise()
  1. It is impossible not to get a 404 item not found since the % of %23 is encoded again.

  2. These tests fail

test-browser/language-variants/view-language-variants.spec.ts

    it(`urls should be encoded correctly`, () => {
        const externalIdWithPoundUrlWithIdLanguage = cmLiveClient.viewLanguageVariant().byItemExternalId('TYPE#A').byLanguageId('xLanguageId').getUrl();
        const externalIdWithEncodedPoundUrlWithIdLanguage = cmLiveClient.viewLanguageVariant().byItemExternalId('TYPE%23A').byLanguageId('xLanguageId').getUrl();
        expect(externalIdWithPoundUrlWithIdLanguage).toEqual(`https://manage.kontent.ai/v2/projects/${testProjectId}/items/TYPE%23A/XItemExternal/variants/xLanguageId`);
        expect(externalIdWithEncodedPoundUrlWithIdLanguage).toEqual(`https://manage.kontent.ai/v2/projects/${testProjectId}/items/TYPE%23A/XItemExternal/variants/xLanguageId`);
    });

Expected behavior

as per test conditions but these are the actual results

externalIdWithPoundUrlWithIdLanguage: https://manage.kontent.ai/v2/projects/b259760f-81c5-013a-05e7-69efb4b954e5/items/external-id/TYPE#A/variants/xLanguageId
externalIdWithEncodedPoundUrlWithIdLanguage: https://manage.kontent.ai/v2/projects/b259760f-81c5-013a-05e7-69efb4b954e5/items/external-id/TYPE%2523A/variants/xLanguageId

Test environment

node

Additional context

Available on kontent support chat. I think that if urlencoding was working properly then it would naturally encode the # and the call should not need to make it safe.

const stringA = 'TYPE#'
const encodedStringA = encodeURIComponent(stringA)
const reEncodeStringA = encodeURIComponent(encodedStringA)

console.log('stringA', stringA)
console.log('encodeStringA', encodedStringA)
console.log('reEncodeStringA', reEncodeStringA)

==> stringA TYPE#
==> encodeStringA TYPE%23
==> reEncodeStringA TYPE%2523

Add support for elements in Asset entity

Motivation

The feature "asset tagging", internally represented as the presence of Taxonomy elements in the Asset entity was introduced to the UI a while ago and now has made its way to the MAPI as well. Now is the time to add support to MAPI SDKs.

Proposed solution

Introduce new property Elements to the Asset entity and make it work. The idea for the API to be exactly the same as elements in language variants, although Asset currently only supports Taxonomy elements that are non-localizable. The API and SDKs, however, should be "generic" enough so that any type element can be easily supported in the future.

Additional context

The feature will be released ~7.3.2022 together with the docs.

The Elements property is being returned from the GET endpoints and is optionally accepted in the POST/PUT endpoints. Only elements that are present during insert/update/upsert will be modified.

Collection is not set when creating new content item using upsertContentItem

Brief bug description

When creating new content item using upsertContentItem providing a collection id, the collection isn't set when creating a new content item. Instead the content item is created with the default collection. I need to make an additional upsertContentItem call for the collection id to 'stick'.

Repro steps

    const contentItemResult = await client
        .upsertContentItem()
        .byItemExternalId(externalId)
        .withData({
            name,
            type: { codename: typeCodename },
            collection: { id: collectionId },
        })
        .toObservable()
        .toPromise()

    console.log(contentItemResult.data.collection.id, collectionId)

Workaround by making another upsert:

    const fixUpsertResult = await client
        .upsertContentItem()
        .byItemExternalId(externalId)
        .withData({
            name,
            collection: { id: collectionId },
        })
        .toObservable()
        .toPromise()

Expected behavior

Expect the collection id provided in the initial upsertContentItem call to stick, instead the result is the default collection ('00000000-0000-0000-0000-000000000000').

Test environment

  • Platform/OS: MacOS
  • Browser: Node
  • Version: v14.14.0

Element - Rich Text blocks not available to configure

Motivation

Currently when Creating the Content Type via code, when it comes to the Rich Text, we can't set the blocks within it.

If you look at the options for allowed_blocks, allowed_text_blocks, allowed_formatting etc... https://docs.kontent.ai/reference/management-api-v2#section/Rich-text-element-in-type

If this was available when performing a Create content type, we could get this type ready for the content editors and restricted to how we need them to be, without having to worry about either adding a Modify or going into the UI to make this change (not what we want to do)

Proposed solution

Include these options for us to use in the JS SDK

Different types for element on content type when retrieving and creating content types

I am using the management client with TypeScript to copy content types between projects. I noticed that the types returned for elements on a content type does not match the types when creating an element on a content type.

The types used to create elements can be found here. But when I retrieve a content type using the SDK the returned element type is ElementContracts.IContentTypeElementContract. I was expecting the element types to match what the docs say here for example.

It would be very helpful if the element type when retrieving a content type had multiple different types or interfaces for each element type. Also it could be useful if the element types used discriminated unions.

Am I using the SDK with TypeScript the wrong way or is there a problem with the types here?

Cannot set `codename` for Content Group

I'm trying to script my models using TypeScript (migrating form JavaScript). When adding a new Content Type using the following code I get an error:

const BrandTypeData = (builder: ContentTypeElementsBuilder
    ): ContentTypeModels.IAddContentTypeData => {
    return {
        name: 'Brand',
        codename: 'brand',
        content_groups: [{
                codename: 'general',
                name: 'General'
            },
            {
                codename: 'seo',
                name: 'SEO'
            }
        ],
        elements: [...

The error I see is:

Type '{ codename: string; name: string; }' is not assignable to type 'IAddContentTypeContentGroup'.
Object literal may only specify known properties, and 'codename' does not exist in type 'IAddContentTypeContentGroup'.ts(2322)

I appreciate my codename here would match the default, but in some cases I wanted to be super-sure what I was getting so that I could add element to the type knowing I have the correct codename. Using vanilla JS for the import worked fine for this, but in TypeScript there is a limitation which looks like it is in the definition for `IAddContentTypeContentGroup

Is there a reason for it missing, or is it an oversight?

Add support for term limitations in Taxonomy element

Motivation

Add support for limiting terms in Taxonomy element in Management API v2

Proposed solution

Enhance response/request models with the properties for term limitation

Additional context

  • Although docs has not been updated yet, until then we might take inspiration from Asset element in type
    image
  • Taxonomy Element property for term limitation should look like this:
 term_count_limit: {
   "value": number,
   "condition": string (Enum: "at_most", "exactly", "at_least")
 }
  • Endpoints:
    • POST v2/projects/{project_id}/types
    • GET v2/projects/{project_id}/types
    • GET v2/projects/{project_id}/types/{type_identifier}
    • PATCH v2/projects/{project_id}/types/{type_identifier}
    • DEL v2/projects/{project_id}/types/{type_identifier}
    • Same for content type snippets
  • Response:
{
    "id": "2d11938a-07ed-45c2-91e9-2635ee27939a",
    "codename": "test_2d11938",
    "last_modified": "2021-07-08T18:01:13.5754404Z",
    "name": "Test",
    "content_groups": [],
    "elements": [
        {
            "guidelines": null,
            "taxonomy_group": {
                "id": "41f013c2-d3a6-4fbf-af1f-466f6326e5c4"
            },
            "is_required": false,
            "term_count_limit": {
                "value": 5,
                "condition": "at_least"
            },
            "type": "taxonomy",
            "id": "08a922b4-8ba4-4f37-8144-f71e69e81a56",
            "codename": "untitled_taxonomy_group"
        }
    ]
}

Add support for collections

Motivation

We have added new endpoints to the Management API for collections and we would like it to be covered in the SDK.

Proposed solution

The SDK should include the functionality of the new endpoints for collections:

  • get all collections
  • patch collections
  • get variants by collections

Additional context

The endpoints that were added:

  • GET /{projectId}/collections
    • returns all collections in the project, no pagination
    • last_modified may be null
    • sample response:
{
    "last_modified": "2021-05-13T14:44:51.1526626Z",
    "collections": [
        {
            "id": "00000000-0000-0000-0000-000000000000",
            "name": "Default",
            "codename": "default"
        },
        {
            "id": "8af21c1b-0bf2-4af4-a26d-5010a4a04339",
            "name": "Development",
            "codename": "development",
            "external_id": "custom-external-id"
        },
       {
            "id": "64c2783c-9982-4ee0-affc-59bbb69a4eff",
            "name": "Marketing",
            "codename": "marketing"
        }
    ]
}
  • PATCH /{projectId}/collections
    • accepts array of operations, similar to asset folders:

      • addInto - to create a new collection, this operation also contains:

        • value:
          • name (required)
          • codename (if not set, it will be generated. Must be unique)
          • externalId (optional, must be unique)
        • after/before (optional, contains identifier of another collection. If it is set, the new collection will be added before/after the specified collection. See i.e. taxonomy patch for example).
      • replace - to modify a collection. Only collection name can be updated this way, because codenames currently cannot be updated. This operation must also contain:

        • reference - identifier of the collection that will be changed
        • property_name
        • value
      • move - to reorder collections. This operation must contain:

        • reference - identifier of the collection that will be changed
        • before or after parameter, saying where the given collection should be moved.
      • remove - to delete a collection:

        • must contain reference - identifier of the collection that will be deleted
        • only an empty collection (that does not have any items) can be deleted
        • default collection cannot be deleted
        • returns code 204 (as in other entities)
    • returns list of all collections - same format as in GET /{projectId}/collections

    • sample request body containing all operations:

[
  {
    "op": "addinto",
    "value": {
      "external_id": "another-collection",
      "name": "Another collection",
      "codename": "another_collection_codename"
    },
    "after": {
      "codename": "second_collection"
    }
  },
  {
    "op": "move",
    "reference" : {
      "codename": "important_collection"
    },
    "before": {
      "codename": "first_collection"
    }
  },
  {
    "op": "remove",
    "reference" : {
      "codename": "extra_collection"
    }
  },
  {
    "op": "replace",
    "property_name": "name",
    "value":"A new name",
    "reference" : {
      "codename": "second_collection"
    }
  }
]
  • GET /{projectId}/collections/{identifier}/variants
    • returns paginated list of variants that are in the specified collection similar to list language variants by type
    • the identifier of the collection can be:
      • {itemId}
      • external-id/{externalId}
      • codename/{codename}
    • sample response:
{
  "variants": [
    {
      "elements": [
        {
          "element": {
            "id": "7117ada8-409f-442c-9d9c-41424980c0a0"
          },
          "value": "Compellingly Develop Scalable Scenarios."
        },
        {
          "element": {
            "id": "3ab5ca51-b317-459b-b34f-401c7cc350cd"
          },
          "value": [
            {
              "id": "7c0dd64a-239d-4ff8-8e9c-29ee09759160"
            }
          ]
        }
      ],
      "workflow_step": {
        "id": "502a6d13-4e33-4e14-905b-c784217ddc7d"
      },
      "item": {
        "id": "3f4a537b-b755-49b0-bb09-9df63ae92404"
      },
      "language": {
        "id": "00000000-0000-0000-0000-000000000000"
      },
      "last_modified": "2021-05-07T08:34:00.7956931Z"
    }
  ],
  "pagination": {
    "continuation_token": null,
    "next_page": null
  }
}

Adding support for scheduled unpublish in MAPIv2

Motivation

It will be possible to schedule unpublish or unpublish and archive a content item variant using Management API V2 via a new endpoint. There will also be a new endpoint for canceling the scheduled unpublish. The old /unpublish route should be marked as deprecated. Release is planned for 9.12.2020 and the feature should be available in SDK as well.

Additional context

Changes are concerning only MAPI v2. MAPI v1 is not changed.

What has changed in MAPI V2

Unpublish and archive endpoint:

  • there is a new route for scheduled unpublish or unpublish and archive:
    PUT /{project_id}/items/{item_identifier}/variants/{language_identifier}/unpublish-and-archive
  • it has an optional JSON parameter in body:
    {"scheduled_to":"2021-11-27T10:22:00"}
    If the parameter is set, the variant will be scheduled to unpublish on the given date, if it is not set, variant will be unpublished immediately.
  • it works exactly as the UI - after unpublishing, the variant is moved to Archived workflow step. This is a change from the current /unpublish route.
  • if some preconditions are not met (variant must exist; it must be Scheduled to publish or have a published version; the date must be valid; the date must be in the future; if the variant is scheduled to publish, the unpublish date must be after planned Publish date), the endpoint will return status code 400 or 404. Otherwise, the endpoint returns status code 204 with empty body, same as /publish

Current unpublish endpoint:

  • the existing unpublish endpoint:
    PUT /{project_id}/items/{item_identifier}/variants/{language_identifier}/unpublish
    should be marked as deprecated.

Cancel scheduled unpublish endpoint:

  • there is a new route for canceling scheduled unpublish of a variant:
    PUT /{project_id}/items/{item_identifier}/variants/{language_identifier}/cancel-scheduled-unpublish
  • it has no parameters in body
  • if it is called on an item that has unpublish date scheduled, the scheduled unpublish will be canceled and the unpublish date deleted.
  • if some preconditions are not met (variant must exist; it must have an unpublish date), the enpoint will return status code 404 or 400. Otherwise, the endpoint returns status code 204 with empty body, same as /cancel-scheduled-publish

Add workflow step codename to MAPI

Motivation

Workflow step codenames has been introduced.

Proposed solution

  1. Add codename to the step response model in GET/{project_id}/workflow
  2. Add a codename alternative to all the endpoints that allow setting a specific workflow step

Additional context

  1. Response model of GET/{project_id}/workflow has been updated to contain workflow step codename. It is changed from the list of return new WorkflowStepResponseModel { Id = workflowStatus.Id, Name = workflowStatus.Name, TransitionsTo = workflowStatus.TransitionsTo, }; to the list of return new WorkflowStepResponseModel { Id = workflowStatus.Id, Name = workflowStatus.Name, CodeName = workflowStatus.CodeName, TransitionsTo = workflowStatus.TransitionsTo, };
  2. For each PUT endpoint that was meant to be used to move variant to a specific workflow step, we have added an alternative using the codename. See the current list of endpoints:
    `[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/{itemId:Guid}/variants/{variantId:Guid}/workflow/{workflowStatusId:Guid}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/codename/{itemCodename}/variants/{variantId:Guid}/workflow/{workflowStatusId:Guid}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/external-id/{externalId}/variants/{variantId:Guid}/workflow/{workflowStatusId:Guid}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/{itemId:Guid}/variants/codename/{languageCodename}/workflow/{workflowStatusId:Guid}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/codename/{itemCodename}/variants/codename/{languageCodename}/workflow/{workflowStatusId:Guid}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/external-id/{externalId}/variants/codename/{languageCodename}/workflow/{workflowStatusId:Guid}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/{itemId:Guid}/variants/{variantId:Guid}/workflow/codename/{workflowStatusCodename}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/codename/{itemCodename}/variants/{variantId:Guid}/workflow/codename/{workflowStatusCodename}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/external-id/{externalId}/variants/{variantId:Guid}/workflow/codename/{workflowStatusCodename}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/{itemId:Guid}/variants/codename/{languageCodename}/workflow/codename/{workflowStatusCodename}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/codename/{itemCodename}/variants/codename/{languageCodename}/workflow/codename/{workflowStatusCodename}")]

[Route("v{version:apiVersion}/projects/{" + RoutingConstants.ProjectId + "}/items/external-id/{externalId}/variants/codename/{languageCodename}/workflow/codename/{workflowStatusCodename}")]`

Automatic retry of 429 error in Content Management SDK

Motivation

The default retry policy in Content Management SDK could retry the 429 errors and take into consideration the retry-after header value. This way I get the maximum possible rate of requests to Kentico Cloud without implementing my own retry policy.

Proposed solution

Retry 429 errors based on retry-after header value.

Add support for subscription projects

Motivation

We have created a subscription-scoped part of the management API, called Subscription API (SAPI) for subscription admins and we would like it to be covered in the SDK.

(!) Going to be released in the first half of January 22.

Proposed solution

The SDK should include functionality of the new endpoints for subscription projects:

  • get all subscription projects

Additional context

SAPI endpoins contract

See Docs preview for more detailed info.

The endpoints that were added:

  • GET /v2/subscriptions/{subscription_id}/projects
    • get all subscription users including their assignment to projects
    • with pagination
    • sample response body:
{
  "projects":[
    {
      "id":"a7d24131-b0c5-4dda-ad78-c0b409951493",
      "name":"Sample project",
      "is_active":true,
      "environments":[
        {
          "id":"c9bad3b5-2b91-4df9-9d4a-53d0bf14343b",
          "name":"Production"
        },
        {
          "id":"fcd4e8cb-4577-4bd0-9104-07538b64eef7",
          "name":"Dev"
        }
      ]
    }
  ],
  "pagination":{
    "continuation_token":"+RID:~...",
    "next_page":"https://manage.kontent.ai/v2/subscriptions/<subscription_id>/<resource>?continuationToken=%2bRID%3a~..."
  }
}

Unable to set `content_group` for elements based on Content Type Snippets

I'm trying to script my models using TypeScript (migrating form JavaScript). When adding a Content Type Snippet using the following code I get an error:

builder.snippetElement({
    snippet: {
        codename: "metadata"
    },
    type: 'snippet',
    content_group: { 
        codename: 'seo'
    }
})

The error I see is:

Argument of type '{ snippet: { codename: string; }; type: "snippet"; content_group: { codename: string; }; }' is not assignable to parameter of type 'ISnippetInType'.
Object literal may only specify known properties, and 'content_group' does not exist in type 'ISnippetInType'.ts(2345)

Using vanilla JS for the import worked fine for this, but in TypeScript there is a limitation. Looks like it is in the definition for `ISnippetInType, where the following line would help:

content_group?: SharedContracts.IReferenceObjectContract;

Is there a reason for it missing, or is it an oversight?

Error Propagation

Brief bug description

I'm using the Management SDK with the Kontent CLI in order to script the creation of my content model. I've based this on the content migration biolerplate using the JS rather than the TS example. The issues is that exceptions are not propagated.

At the moment I have a work around courtesy of @Simply007 using hte debugger, but longer term we need the error to be raised back up to the user.

Repro steps

  1. Script a new JS import to create a simple type. Run this migration and check that the type is created. Script simiarl tothe below:
const migration = {
    order: 2,
    run: async(apiClient) => {
        await apiClient
            .addContentType()
            .withData(BrandTypeData)
            .toPromise();
    }
};

const BrandTypeData = (builder) => {
    return {
        name: 'Brand',
        codename: 'brand',
        elements: [
            builder.textElement({
                name: 'Name',
                codename: 'name',
                type: 'text'
            })
        ]
    };
};

module.exports = migration;
  1. Modify status.json to remove that file from the log.
  2. Re-run the migration. This should fail because the type already exists.
  3. Error show to the user will be similar to:
An error occurred while running migration: 06_grr_init_createRestaurantContentType.js see the output from running the script.
Message: call.mapError is not a function
Execution of the "06_grr_init_createRestaurantContentType.js" migration was not successful, stopping...

Expected behavior

Error message is displayed to the user, allowing them to correct any issues.

Test environment

  • Platform/OS: Windows 10 Pro (20H2)
  • Version [e.g. 22]
  • Node: v14.15.4
  • npm: 6.14.10
  • @kontent/kontent-cli: 0.0.10
  • @kontnet/kontent-management: 0.4.10
  • rxjs: 7.0.0

Additional context

As mentions, based on the boilerplate migrations using the JS example.

Screenshots

image

Publishing with CM requires date in the future

Brief bug description

When attempting to use publishOrScheduleLanguageVariant(), I cannot publish now or at the next scheduled publish

Repro steps

A few cases

  1. Use publishOrScheduleLanguageVariant without .withData -> type error -- is not a function
  2. Use publishOrScheduleLanguageVariant with empty .withData -> 404 -- "The provided value is not a valid date."
  3. Use publishOrScheduleLanguageVariant with date in the past in .withData -> 400 -> "The provided date value needs to be in the future."

Expected behavior

For each case, I expect:

  1. The ability not to include data
  2. The ability not to specific a time
  3. The ability to provide a time in the past and get a 204, as described in the documentation: https://developer.kenticocloud.com/reference#content-management-api-v2-publish-language-variant

Test environment

Firefox

Wrong URL and method for modifyContentType()

Brief bug description

Trying to update a content type using the code below attempts to call the incorrect URL with the wrong method.

client
  .modifyContentType()
  .byTypeCodename(...)
  .withData(...)

The docs show the full url as https://manage.kontent.ai/v2/projects/{project_id}/types/{type_identifier} and it should be a PATCH

However, it is calling https://manage.kontent.ai/v2/projects/{project_id}/types as a POST resulting in error:

Codename โ€˜{codename}โ€™ is not unique.

Unable to stop errors from being logged/Errors thrown are different values

Brief bug description

When catching an error with the management client, the axios error is printed to the console.

The error caught also has a different value to that thrown; I am unable to reach, too.

Given the following block, which is attempting to programatically add new items to the content model:

image

This error is still printed to the terminal:

image

The static method handleKontentError looks like this:

image

So I'm confident that this isn't the issue.

Logging and stringifying the error in console gives us a different type to the one printed by the management client:

image

Repro steps

  1. call ManagementClient#addContentType.withData.toPromise with an invalid payload
  2. Catch the error
  3. Observe that it's still printed to the terminal
  4. Observe that you have a different error

Expected behavior

What the correct behavior is?

Test environment

  • Platform/OS: Node 15.5.1; Typescript 4.4.4; @kentico/[email protected]
  • Browser n/a
  • Version [e.g. 22]

Additional context

Have tried toPromise().catch(), but the behaviour still exists.

Screenshots

See above

Using POST and not PATCH to modify languages

Brief bug description

When attempting to use modifyLanguage(), the SDK uses POST, which is the wrong method for the endpoint.

Expected behavior

The request will be sent as a PATCH request.

id is not assignable to IRichTextComponent

Brief bug description

When using the languageVariantElementsBuilder in v1.3.0 of the SDK, the id property of a component object in a richTextElement throws error:

Type '{ id: string; type: { codename: string; }; elements: { element: { codename: string; }; value: string; }[]; }' is not assignable to type 'IRichTextComponent'.
Object literal may only specify known properties, and 'id' does not exist in type 'IRichTextComponent'.ts(2322)

Repro steps

  1. Open the Kontent Migrations Boilerplate
  2. Add the following code to a new TypeScript file in the project:
.upsertLanguageVariant()
.byItemId(test)
.byLanguageCodename('default')
    .withData((builder) => [
      builder.richTextElement({
          element:{
              codename:"my_test_rte"
          },
          value:'<object type="application/kenticocloud" data-type="component" data-id="382abced-bfb6-4ee9-a2d4-2c3b8cd8ba5d"></object>',
          components: [
              {
                  id: "382abced-bfb6-4ee9-a2d4-2c3b8cd8ba5d",
                  type: {
                      codename:"article",
                  },
                  elements: [
                      {
                          element: {
                              codename: "my_text_element",
                          },
                          value: "Here is the text",
                      },
                  ],
              }
          ]
      })
      ])
      .toPromise()

Expected behavior

No IDE warning and id exists for IRichTextComponent per the M API documentation: https://docs.kontent.ai/reference/management-api-v2#section/Rich-text-element

Additional context

Probably should be added here: https://github.com/Kentico/kontent-management-sdk-js/blob/914e022a58adb2c7b4e68980041087c75b459235/lib/models/language-variants/language-variant-elements-builder.ts#L9

Add support for project users

Motivation

We have added new endpoints to the Management API for project users and we would like it to be covered in the SDK.
(!) Going to be released in the first half of January 22.

Proposed solution

The SDK should include the functionality of the new endpoints for users:

  • invite user into a project
  • change user roles

Additional context

See Docs preview for more detailed info.

The endpoints that were added:

  • POST /{project_id}/users
    • invites a user into the project
    • sample request body:
{
  "email":"[email protected]",
  "collection_groups":[
    {
      "collections":[
        {
          "id":"00000000-0000-0000-0000-000000000000"
        },
        {
          "codename":"collection-1"
        }
      ],
      "roles":[
        {
          "id":"f58733b9-520b-406b-9d45-eb15a2baee96",
          "languages":[
            {
              "codename":"english"
            }
          ]
        }
      ]
    }
  ]
}
  • sample response:
{
 "user_id":"d94bc87a-c066-48a1-a910-4f991ccc1fb5",
 "collection_groups":[
   {
     "collections":[
       {
         "id":"00000000-0000-0000-0000-000000000000"
       },
       {
         "id":"28b68213-d636-4b01-9fd1-988b93789e17"
       }
     ],
     "roles":[
       {
         "id":"f58733b9-520b-406b-9d45-eb15a2baee96",
         "languages":[
           {
             "id":"7df9a691-cf29-402d-9598-66273e7561b7"
           }
         ]
       }
     ]
   }
 ]
}
  • PUT /{project_id}/users/{user_identifier}/roles
    • changes the users' roles and assignment to collections and languages i current project
    • sample request body:
{
  "collection_groups":[
    {
      "collections":[
        {
          "id":"00000000-0000-0000-0000-000000000000"
        },
        {
          "codename":"collection-1"
        }
      ],
      "roles":[
        {
          "id":"f58733b9-520b-406b-9d45-eb15a2baee96",
          "languages":[
            {
              "codename":"english"
            }
          ]
        }
      ]
    }
  ]
}
  • sample response:
{
 "user_id":"d94bc87a-c066-48a1-a910-4f991ccc1fb5",
 "collection_groups":[
   {
     "collections":[
       {
         "id":"00000000-0000-0000-0000-000000000000"
       },
       {
         "id":"28b68213-d636-4b01-9fd1-988b93789e17"
       }
     ],
     "roles":[
       {
         "id":"f58733b9-520b-406b-9d45-eb15a2baee96",
         "languages":[
           {
             "id":"7df9a691-cf29-402d-9598-66273e7561b7"
           }
         ]
       }
     ]
   }
 ]
}

Add support for project roles

Motivation

We have added new endpoints to the Management API for project roles and we would like it to be covered in the SDK.
(!) Going to be released in the first half of January 22.

Proposed solution

The SDK should include the functionality of the new endpoints for roles:

  • get all roles
  • get role by identifier

Additional context

See Docs preview for more detailed info.

The endpoints that were added:

  • GET /{projectId}/roles
    • returns all roles in the project, no pagination
    • sample response:
{
  "roles":[
    {
      "id":"7dedb656-aef7-40ae-8158-98bbf3542f4a",
      "name":"Project manager",
      "codename":"project-manager"
    },
    {
      "id":"ee483b59-5a24-4010-b277-ae224c34bc71",
      "name":"Custom role"
    }
  ]
}
  • GET /{project_id}/roles/{role_identifier}
    • returns role by its identifier (id or codename)
    • sample response:
{
  "id":"7dedb656-aef7-40ae-8158-98bbf3542f4a",
  "name":"Project manager",
  "codename":"project-manager"
}

Add support for subscription users

Motivation

We have created a subscription-scoped part of the management API, called Subscription API (SAPI) for subscription admins and we would like it to be covered in the SDK.

(!) Going to be released in the first half of January 22.

Proposed solution

The SDK should include functionality of the new endpoints for subscription users:

  • get all subscription users
  • get subscription user by identifier
  • activate user in all subscription projects
  • deactivate user in all subscription projects

Additional context

SAPI endpoins contract

See Docs preview for more detailed info.

The endpoints that were added:

  • GET /v2/subscriptions/{subscription_id}/users
    • get all subscription users including their assignment to projects
    • with pagination
    • sample response body:
{
  "users":[
    {
      "id":"e67eadda-dc58-4bf9-89fc-1dadc0b95858",
      "first_name":"John",
      "last_name":"Doe",
      "email":"[email protected]",
      "has_pending_invitation":false,
      "projects":[
        {
          "id":"a7d24131-b0c5-4dda-ad78-c0b409951493",
          "name":"Sample project",
          "environments":[
            {
              "id":"c9bad3b5-2b91-4df9-9d4a-53d0bf14343b",
              "name":"Production",
              "is_user_active":true,
              "last_activity_at":"2021-12-24T12:00:00.7692802Z",
              "collection_groups":[
                {
                  "collections":[
                    {
                      "id":"3f367e4f-75b7-4b48-be3b-1136bbaf1f53"
                    }
                  ],
                  "roles":[
                    {
                      "id":"c11a2c9e-f129-43b8-9cb1-20a7b3746934",
                      "name":"Project manager",
                      "codename":"project-manager",
                      "languages":[
                        {
                          "id":"00000000-0000-0000-0000-000000000000",
                          "external_id":"string",
                          "codename":"default",
                          "name":"Default language",
                          "is_active":true
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "pagination":{
    "continuation_token":"+RID:~...",
    "next_page":"https://manage.kontent.ai/v2/subscriptions/<subscription_id>/<resource>?continuationToken=%2bRID%3a~..."
  }
}
  • GET /v2/subscriptions/{subscription_id}/users/{user_identifier}
    • return subscription user by his id or email including the assignment to projects
    • user identifier can be
      • {user_id}
      • email/{email}
    • sample response body:
{
 "id":"e67eadda-dc58-4bf9-89fc-1dadc0b95858",
 "first_name":"John",
 "last_name":"Doe",
 "email":"[email protected]",
 "has_pending_invitation":false,
 "projects":[
   {
     "id":"a7d24131-b0c5-4dda-ad78-c0b409951493",
     "name":"Sample project",
     "environments":[
       {
         "id":"c9bad3b5-2b91-4df9-9d4a-53d0bf14343b",
         "name":"Production",
         "is_user_active":true,
         "last_activity_at":"2021-12-24T12:00:00.7692802Z",
         "collection_groups":[
           {
             "collections":[
               {
                 "id":"3f367e4f-75b7-4b48-be3b-1136bbaf1f53"
               }
             ],
             "roles":[
               {
                 "id":"c11a2c9e-f129-43b8-9cb1-20a7b3746934",
                 "name":"Project manager",
                 "codename":"project-manager",
                 "languages":[
                   {
                     "id":"00000000-0000-0000-0000-000000000000",
                     "external_id":"string",
                     "codename":"default",
                     "name":"Default language",
                     "is_active":true
                   }
                 ]
               }
             ]
           }
         ]
       }
     ]
   }
 ]
}
  • PUT /v2/subscriptions/{subscription_id}/users/{user_identifier}/activate

    • activates the user in all projects in the subscription
    • user identifier can be
      • {user_id}
      • email/{email}
    • returns 204 without body, or 400 / 404 with error object
  • PUT /v2/subscriptions/{subscription_id}/users/{user_identifier}/dectivate

    • deactivates the user in all projects in the subscription
    • user identifier can be
      • {user_id}
      • email/{email}
    • returns 204 without body, or 400 / 404 with error object

Update Item Docs Unclear or Broken

Brief bug description

Updating a kontent item via the management API appears to be broken (or undocumented?)

Repro steps

Use the following code:

const resp = await client
  .updateContentItem()
  .byItemId('<my item id>')
  .withData({
    title: '<my item title>',
    body: '<a new body>', // the field in kontent that I want to update
  })
  .promise()

Expected behavior

The body field in the payload should be reflected in kontent

Test environment

  • Platform/OS: Darwin Harrys-MBP 19.3.0 Darwin Kernel Version 19.3.0: Thu Jan 9 20:58:23 PST 2020; root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64
  • Browser Firefox
  • Version 80.0.1

Additional context

N/A

Screenshots

Console log of resp.debug.response -

image

Add support for 'collection' property in the Content item insert/update endpoints

Motivation

It will be possible to place a new item into a collection using Management API V2. Also, it'll be possible to move an existing item from one collection to another. Release is planned for 30.9.2020 and the feature should be available in SDK as well.

Additional context

  • the support was added to MAPI V2 only
  • the feature might be unavailable depending on the plan. We are able to turn it on per request.
  • the collection property of the content item is of reference type and it is optional. However it is always returned in responses.
  • As it is not possible to create collections via Management API right now, it doesn't make sense to reference a collection using external_id. An error is thrown in this case.

What has changed in MAPI V2

Samples

GET /v2/projects/{project_id}/items/codename/my_article

Response

{
    "id": "33fb603d-9c1c-4216-8384-bd2c9ab432b1",
    "name": "new name",
    "codename": "my_article",
    "type": {
        "id": "6c90efdf-eb70-4ee7-bd5f-352f87992a7f"
    },
    "collection": {
        "id": "c4224458-f24c-4f8e-a719-7faaadbc6e88"
    },
    "sitemap_locations": [],
    "last_modified": "2020-09-18T14:21:26.54877Z"
}

POST /v2/projects/{project_id}/items

Request

{
    "name": "My article",
    "collection": {
        "codename": "marketing"
    },
    "type": {
        "id": "6c90efdf-eb70-4ee7-bd5f-352f87992a7f"
    }
}

PUT /v2/projects/{project_id}/items/codename/my_article

Request

{
  "name": "My article updated",
  "collection": {
      "codename": "marketing"
  }
}

Requests withoutData include header Content-Type: application/x-www-form-urlencoded

Brief bug description

Sending PUT/POST request with empty body (.withoutData()) sends content-type header: "application/x-www-form-urlencoded".
Some servers/endpoints might return 415 Unsupported Media Type for this type of requests.
This is a different approach as .NET sdk has. .NET sdk for empty body requests doesn't send content-type header at all, which is IMHO correct. Postman has a similar approach.

Repro steps

 client.publishLanguageVariant()
  .byItemId('00000000-0000-0000-0000-000000000000')
  .byLanguageId('00000000-0000-0000-0000-000000000000')
    .withoutData()
    .toPromise()
    .then((response) => {
        console.log(response)
        },
    (error) => {
        console.log(error)
    });

In the console, you should be able to see the headers of the requests and there is Content-Type header
image

Expected behavior

The client is not sending a content-type header for requests that don't include the body.

Test environment

  • node: v16.10.0; Windows 10

Allow more robust handling of simultaneous PATCH request operations.

Motivation

When performing a PATCH request, such as modifying a Taxonomy Group, simultaneous actions in the Payload must be able to succeed, or else none of them are successful. For example, here is a taxonomy group in my sample project:
image

I perform a PATCH request (modifyTaxonomy()) that has a payload with the exact same structure as the payload in the example from the above documentation. It's actions, in order, are:

  1. Replace the taxonomy group name.
  2. Replace the taxonomy group codename.
  3. Replace the contents of the First Term taxonomy term.
  4. Remove the Unused Taxonomy Term.
  5. Add a new taxonomy term into the Second-level Taxonomy Term. (Added from step 3.)

All of these steps are successful when the script is run:
image

Then, in Kontent, I manually reset the category back to image one. However, I do not create Unused Taxonomy Term again. If I run the same script, nothing changes. The three preceding Replace actions are not successfully run, and neither is the AddInto action afterwards. The script will not be successful unless I either add the Unused Taxonomy Term, or I remove the Remove action from the script.

Proposed solution

It would be great if there was a way to check the payload for actions that do not need to be performed; while still allowing the rest of the payload's actions to run successfully. This could help in testing larger payloads. Even more so if you wanted to add and test one action at a time, or insert actions between other actions. In other words, I want to perform steps 1-5, but I would build the payload one action at a time. 1, 1-2, 1-3, 1-4, 1-5. Between each addition, you would not need to reset the Taxonomy item in order for all of the actions to work.

Add item elements URL generation

Kentico Kontent provides a way of accessing specific content items in Kentico Kontent UI via URL.
Here is information, how to do that:

Blog post showcasing them:

Similarly to kontent-ai/management-sdk-net#9, add functionality for generating content item URLs to the KK Management SDK.

Result:

Blocks:

Add support for environment management

Motivation

As an environment manager/developer, I want to be able to programmatically manage environments so that I don't have to do it all manually in Kontent.

This feature will be released at the beginning of March.

Proposed solution

The SDK should support new functionality:

  • clone environment
  • delete environment
  • rename environment
  • get environment cloning state
  • mark environment as production

Additional info:
https://kentico.atlassian.net/wiki/spaces/KCP/pages/3507027988/MAPI+support+for+environment+management

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.