private ItemIdValue createWikidataItem(final String resourceURI, final ItemDocument wikidataItem)
      throws WikidataImporterException {

    // create Item at Wikibase (to have a generated Item identifier)
    try {

      final Observable<Response> createEntityResponse =
          wikibaseAPIClient.createEntity(
              wikidataItem, WikibaseAPIClient.WIKIBASE_API_ENTITY_TYPE_ITEM);

      final JsonNode entityOrErrorJSON =
          processEditEntityResponse(
              resourceURI, createEntityResponse, WikibaseAPIClient.WIKIBASE_API_ENTITY_TYPE_ITEM);

      final JsonNode errorNode = entityOrErrorJSON.get(MEDIAWIKI_ERROR_IDENTIFIER);

      if (errorNode == null) {

        // response JSON should be an entity

        final ItemDocument itemDocument =
            MAPPER.treeToValue(entityOrErrorJSON, JacksonItemDocument.class);

        if (itemDocument == null) {

          final String message =
              String.format(
                  "could not create new item for '%s'; could not deserialize response body",
                  resourceURI);

          LOG.error(message);

          throw new WikidataImporterException(message);
        }

        final ItemIdValue responseItemId = itemDocument.getItemId();

        if (responseItemId == null) {

          final String message =
              String.format(
                  "could not create new item for '%s'; response property id is not available",
                  resourceURI);

          LOG.error(message);

          throw new WikidataImporterException(message);
        }

        return responseItemId;
      }

      // TODO: refactoring following code and that one of property creation duplicate handling into
      // separate method

      // an error occurred

      final JsonNode errorCodeJSON = errorNode.get(MEDIAWIKI_CODE_IDENTIFIER);

      if (errorCodeJSON == null) {

        final String message =
            String.format(
                "could not create new item for '%s'; an unknown error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final String errorCode = errorCodeJSON.asText();

      if (!MEDIAWIKI_MODIFICATION_FAILED_ERROR_CODE.equals(errorCode)) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final JsonNode messagesJSON = errorNode.get(MEDIAWIKI_MESSAGES_IDENTIFIER);

      if (messagesJSON == null || messagesJSON.size() <= 0) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final JsonNode firstMessageNode = messagesJSON.get(0);

      if (firstMessageNode == null) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final JsonNode errorMessageNameNode = firstMessageNode.get(MEDIAWKI_NAME_IDENTIFIER);

      final String errorMessageName = errorMessageNameNode.asText();

      if (!WIKIBASE_VALIDATOR_LABEL_WITH_DESCRIPTION_CONFLICT_ERROR_MESSAGE_NAME.equals(
          errorMessageName)) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final JsonNode errorMessageParametersNode =
          firstMessageNode.get(MEDIAWIKI_PARAMETERS_IDENTIFIER);

      if (errorMessageParametersNode == null || errorMessageParametersNode.size() < 3) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final JsonNode thirdErrorMessageParameterNode = errorMessageParametersNode.get(2);

      if (thirdErrorMessageParameterNode == null) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      // extract the item id from this value
      final String thirdErrorMessageParameter = thirdErrorMessageParameterNode.asText();

      final Optional<String> optionalItemId = findItemId(thirdErrorMessageParameter);

      if (!optionalItemId.isPresent()) {

        final String message =
            String.format(
                "could not create new item for '%s'; an error ('%s') occurred",
                resourceURI, MAPPER.writeValueAsString(errorNode));

        throw new WikidataImporterException(message);
      }

      final String itemId = optionalItemId.get();

      return Datamodel.makeItemIdValue(itemId, null);
    } catch (final WikidataImporterException e) {

      throw e;
    } catch (final Exception e) {

      final String message = "something went wrong, while trying to create a new item";

      LOG.error(message, e);

      throw new WikidataImporterException(message, e);
    }
  }
  private PropertyIdValue createOrGetWikidataProperty(
      final String propertyIdentifier, final String propertyValueDataType) {

    return gdmPropertyURIWikidataPropertyMap.computeIfAbsent(
        propertyIdentifier,
        propertyIdentifier1 -> {
          final List<MonolingualTextValue> labels = generateLabels(propertyIdentifier1);
          final List<MonolingualTextValue> descriptions = generateLabels(propertyIdentifier1);
          final List<MonolingualTextValue> aliases = new ArrayList<>();

          // add datatype - e.g. all literals are strings (DatatypeIdValue#DT_STRING) and all
          // resources are items (DatatypeIdValue#DT_ITEM)
          final DatatypeIdValue datatypeIdValue =
              Datamodel.makeDatatypeIdValue(propertyValueDataType);

          // note: list of descriptions cannot be null
          // note: list of aliases cannot be null
          final PropertyDocument wikidataProperty =
              Datamodel.makePropertyDocument(null, labels, descriptions, aliases, datatypeIdValue);

          // create Property at Wikibase (to have a generated Property identifier)
          try {

            final Observable<Response> createEntityResponse =
                wikibaseAPIClient.createEntity(
                    wikidataProperty, WikibaseAPIClient.WIKIBASE_API_ENTITY_TYPE_PROPERTY);

            // handle duplicates, i.e., one can only create uniquely labelled properties in
            // wikibase, otherwise "wikibase-validator-label-conflict" will be thrown
            final JsonNode entityOrErrorJSON =
                processEditEntityResponse(
                    propertyIdentifier1,
                    createEntityResponse,
                    WikibaseAPIClient.WIKIBASE_API_ENTITY_TYPE_PROPERTY);

            final JsonNode errorNode = entityOrErrorJSON.get(MEDIAWIKI_ERROR_IDENTIFIER);

            if (errorNode == null) {

              // response JSON should be an entity

              final PropertyDocument propertyDocument =
                  MAPPER.treeToValue(entityOrErrorJSON, JacksonPropertyDocument.class);

              if (propertyDocument == null) {

                final String message =
                    String.format(
                        "could not create new property for '%s'; could not deserialize response body",
                        propertyIdentifier1);

                LOG.error(message);

                throw new WikidataImporterException(message);
              }

              final PropertyIdValue responsePropertyId = propertyDocument.getPropertyId();

              if (responsePropertyId == null) {

                final String message =
                    String.format(
                        "could not create new property for '%s'; response property id is not available",
                        propertyIdentifier1);

                LOG.error(message);

                throw new WikidataImporterException(message);
              }

              return responsePropertyId;
            }

            // TODO: refactoring following code and that one of item creation duplicate handling
            // into separate method

            // an error occurred

            final JsonNode errorCodeJSON = errorNode.get(MEDIAWIKI_CODE_IDENTIFIER);

            if (errorCodeJSON == null) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an unknown error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final String errorCode = errorCodeJSON.asText();

            if (!MEDIAWIKI_FAILED_SAVE_ERROR_CODE.equals(errorCode)) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final JsonNode messagesJSON = errorNode.get(MEDIAWIKI_MESSAGES_IDENTIFIER);

            if (messagesJSON == null || messagesJSON.size() <= 0) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final JsonNode firstMessageNode = messagesJSON.get(0);

            if (firstMessageNode == null) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final JsonNode errorMessageNameNode = firstMessageNode.get(MEDIAWKI_NAME_IDENTIFIER);

            final String errorMessageName = errorMessageNameNode.asText();

            if (!WIKIBASE_VALIDATOR_LABEL_CONFLICT_ERROR_MESSAGE_NAME.equals(errorMessageName)) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final JsonNode errorMessageParametersNode =
                firstMessageNode.get(MEDIAWIKI_PARAMETERS_IDENTIFIER);

            if (errorMessageParametersNode == null || errorMessageParametersNode.size() < 3) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final JsonNode thirdErrorMessageParameterNode = errorMessageParametersNode.get(2);

            if (thirdErrorMessageParameterNode == null) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            // extract the property id from this value
            final String thirdErrorMessageParameter = thirdErrorMessageParameterNode.asText();

            final Optional<String> optionalPropertyId = findPropertyId(thirdErrorMessageParameter);

            if (!optionalPropertyId.isPresent()) {

              final String message =
                  String.format(
                      "could not create new property for '%s'; an error ('%s') occurred",
                      propertyIdentifier1, MAPPER.writeValueAsString(errorNode));

              throw new WikidataImporterException(message);
            }

            final String propertyId = optionalPropertyId.get();

            return Datamodel.makePropertyIdValue(propertyId, null);
          } catch (final WikidataImporterException e1) {

            throw WikidataImporterError.wrap(e1);
          } catch (final Exception e) {

            final String message2 = "something went wrong, while trying to create a new property";

            throw WikidataImporterError.wrap(new WikidataImporterException(message2, e));
          }
        });
  }