javajsonmagentoapache-commons-httpclientmagento-rest-api

Java - Magento2 rest api product upload with image silently fails


I have a magento CE 2.1.2 storefront set up. I'd like to use Java to upload products to my store using the Magento REST API. I have managed to get 2 leg OAuth set up using Apache-commons-httpclient, and product upload actually works quite well. The problem comes when I try to upload an image with the product.

From the official catalogProductRepositoryV1 Magento docs, the json syntax is: (the important part being the "media_gallery_entries" bit)

{
  "product": {
    "id": 0,
    "sku": "string",
    "name": "string",
    "attribute_set_id": 0,
    "price": 0,
    "status": 0,
    "visibility": 0,
    "type_id": "string",
    "created_at": "string",
    "updated_at": "string",
    "weight": 0,
    "extension_attributes": {
      "bundle_product_options": [
        {
          "option_id": 0,
          "title": "string",
          "required": true,
          "type": "string",
          "position": 0,
          "sku": "string",
          "product_links": [
            {
              "id": "string",
              "sku": "string",
              "option_id": 0,
              "qty": 0,
              "position": 0,
              "is_default": true,
              "price": 0,
              "price_type": 0,
              "can_change_quantity": 0,
              "extension_attributes": {}
            }
          ],
          "extension_attributes": {}
        }
      ],
      "downloadable_product_links": [
        {
          "id": 0,
          "title": "string",
          "sort_order": 0,
          "is_shareable": 0,
          "price": 0,
          "number_of_downloads": 0,
          "link_type": "string",
          "link_file": "string",
          "link_file_content": {
            "file_data": "string",
            "name": "string",
            "extension_attributes": {}
          },
          "link_url": "string",
          "sample_type": "string",
          "sample_file": "string",
          "sample_file_content": {
            "file_data": "string",
            "name": "string",
            "extension_attributes": {}
          },
          "sample_url": "string",
          "extension_attributes": {}
        }
      ],
      "downloadable_product_samples": [
        {
          "id": 0,
          "title": "string",
          "sort_order": 0,
          "sample_type": "string",
          "sample_file": "string",
          "sample_file_content": {
            "file_data": "string",
            "name": "string",
            "extension_attributes": {}
          },
          "sample_url": "string",
          "extension_attributes": {}
        }
      ],
      "stock_item": {
        "item_id": 0,
        "product_id": 0,
        "stock_id": 0,
        "qty": 0,
        "is_in_stock": true,
        "is_qty_decimal": true,
        "show_default_notification_message": true,
        "use_config_min_qty": true,
        "min_qty": 0,
        "use_config_min_sale_qty": 0,
        "min_sale_qty": 0,
        "use_config_max_sale_qty": true,
        "max_sale_qty": 0,
        "use_config_backorders": true,
        "backorders": 0,
        "use_config_notify_stock_qty": true,
        "notify_stock_qty": 0,
        "use_config_qty_increments": true,
        "qty_increments": 0,
        "use_config_enable_qty_inc": true,
        "enable_qty_increments": true,
        "use_config_manage_stock": true,
        "manage_stock": true,
        "low_stock_date": "string",
        "is_decimal_divided": true,
        "stock_status_changed_auto": 0,
        "extension_attributes": {}
      },
      "configurable_product_options": [
        {
          "id": 0,
          "attribute_id": "string",
          "label": "string",
          "position": 0,
          "is_use_default": true,
          "values": [
            {
              "value_index": 0,
              "extension_attributes": {}
            }
          ],
          "extension_attributes": {},
          "product_id": 0
        }
      ],
      "configurable_product_links": [
        0
      ]
    },
    "product_links": [
      {
        "sku": "string",
        "link_type": "string",
        "linked_product_sku": "string",
        "linked_product_type": "string",
        "position": 0,
        "extension_attributes": {
          "qty": 0
        }
      }
    ],
    "options": [
      {
        "product_sku": "string",
        "option_id": 0,
        "title": "string",
        "type": "string",
        "sort_order": 0,
        "is_require": true,
        "price": 0,
        "price_type": "string",
        "sku": "string",
        "file_extension": "string",
        "max_characters": 0,
        "image_size_x": 0,
        "image_size_y": 0,
        "values": [
          {
            "title": "string",
            "sort_order": 0,
            "price": 0,
            "price_type": "string",
            "sku": "string",
            "option_type_id": 0
          }
        ],
        "extension_attributes": {}
      }
    ],
    "media_gallery_entries": [
      {
        "id": 0,
        "media_type": "string",
        "label": "string",
        "position": 0,
        "disabled": true,
        "types": [
          "string"
        ],
        "file": "string",
        "content": {
          "base64_encoded_data": "string",
          "type": "string",
          "name": "string"
        },
        "extension_attributes": {
          "video_content": {
            "media_type": "string",
            "video_provider": "string",
            "video_url": "string",
            "video_title": "string",
            "video_description": "string",
            "video_metadata": "string"
          }
        }
      }
    ],
    "tier_prices": [
      {
        "customer_group_id": 0,
        "qty": 0,
        "value": 0,
        "extension_attributes": {}
      }
    ],
    "custom_attributes": [
      {
        "attribute_code": "string",
        "value": "string"
      }
    ]
  },
  "saveOptions": true
}

The methods I'm running:

public void uploadItem(ItemType item) throws UploadException {      
    try {
        HttpPost postRequest = new HttpPost("http://my.magento.storefront/index.php/rest/V1/products");
        postRequest.addHeader("Content-Type", "application/json");
        postRequest.addHeader("Authorization", String.format("Bearer %s", authToken));
        String jsonItem = itemToJson(item);
        System.out.println(jsonItem);
        postRequest.setEntity(new StringEntity(jsonItem));
        HttpResponse postResponse = httpClient.execute(postRequest);
        System.out.println();
        System.out.println(EntityUtils.toString(postResponse.getEntity()));
    } catch (ParseException | IOException | JSONException e) {
        e.printStackTrace();
        throw new UploadException(e);
    }
}


private String imagesToJson(ItemType item) throws IOException {
    String imageUrl = "http://my.image.jpeg"; //pulled from item in production
    String encodedImage = null;
    URL url = new URL(imageUrl);
    try(InputStream in = new BufferedInputStream(url.openStream());
            ByteArrayOutputStream out = new ByteArrayOutputStream()) {

            byte[] buf = new byte[512];
            for (int count = in.read(buf); count >= 0; count = in.read(buf)) {
                out.write(buf, 0, count);
            }
            byte[] response = out.toByteArray(); //sane, I tried saving to a file and checking
            encodedImage = Base64.getEncoder().encodeToString(response);
    }

    String picName = item.getItemID() + ".jpg";

    return new JSONArray().put(
            new JSONObject()
                .put("position", 0)
                .put("media_type", "image")
                .put("disabled", "false")
                .put("types", new JSONArray()
                        .put("image")
                        .put("small_image")
                        .put("thumbnail"))
                .put("label", picName)
                .put("content", new JSONObject()
                        .put("base64_encoded_data", encodedImage)
                        .put("type", "image/jpeg")
                        .put("name", picName))
            ).toString();
}


private String itemToJson(ItemType item) throws JSONException, IOException {
    return new JSONObject().put("saveOptions", "true").put("product", 
            new JSONObject()
        .put("sku", item.getItemID())
        .put("name", item.getTitle())
        .put("attribute_set_id", 4)
        .put("price", item.getSellingStatus().getCurrentPrice().getValue())
        .put("status", 0)
        .put("visibility", 0)
        .put("type_id", "simple")
        .put("weight", item.getShippingDetails().getCalculatedShippingRate().getWeightMajor().getValue())
        .put("media_gallery_entries", imagesToJson(item)))
    .toString();
}

Using uploadItem(ItemType), the product is uploaded to the catalog. However, there is no image on the store, and I'm given "media_gallery_entries": [] in the response, with no further debugging help!

I think I've implemented everything correctly, so I'm unsure what to do other than SFTP and manually manage the media folder; which just sounds ugly.

Let me know if anyone needs any extra info, or omitted methods/fields.


Solution

  • I solved this by using Gson instead of org.json. I believe the issue was me erroneously using string literals where the server expected otherwise (namely, the boolean values).