fastlane-plugin-sapfire 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/lib/fastlane/plugin/sapfire/actions/associate_ms_store_action.rb +104 -74
- data/lib/fastlane/plugin/sapfire/actions/ms_credentials_action.rb +7 -10
- data/lib/fastlane/plugin/sapfire/actions/upload_ms_store_action.rb +40 -7
- data/lib/fastlane/plugin/sapfire/helper/ms_credentials.rb +47 -0
- data/lib/fastlane/plugin/sapfire/helper/ms_devcenter_helper.rb +117 -62
- data/lib/fastlane/plugin/sapfire/version.rb +1 -1
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 88044ddcaf677c20f2d9e0b7b2a54e1f92b2b609344815c82ff3d26b2344385f
         | 
| 4 | 
            +
              data.tar.gz: 1e060c84cc0da8e87ee0f01aa8e3030745310c6cc97f89ef451a3f760371e615
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c751b85ff40459a46faee5598277794d05e5da1d679e12b4fbb8b261a893824f58bd04ef77acb4b5251288993669414bf4e0ad5ceb276cc76f65901576809f38
         | 
| 7 | 
            +
              data.tar.gz: 2cccb183366378b8987a2f709078409bc967ca2831ef34bd4e2ac56a5214c690b94a32d39d54acfdcf250b8d6207a381206208cb055c38359121727663054f03
         | 
    
        data/README.md
    CHANGED
    
    | @@ -34,9 +34,13 @@ but also can be used independently via command-line interface. | |
| 34 34 | 
             
            **Microsoft Store** is a digital distribution platform for Windows.
         | 
| 35 35 |  | 
| 36 36 | 
             
            ## Getting started
         | 
| 37 | 
            -
            To get started working with plugin, add it to your  | 
| 37 | 
            +
            To get started working with plugin, add it to your project by running:
         | 
| 38 | 
            +
            ```shell
         | 
| 39 | 
            +
            fastlane add_plugin sapfire
         | 
| 40 | 
            +
            ```
         | 
| 41 | 
            +
            Or, if you want to pick the latest commit from the repository, add this to `fastlane/Pluginfile`:
         | 
| 38 42 | 
             
            ```ruby
         | 
| 39 | 
            -
            gem "fastlane-plugin-sapfire", git: "https://github.com/fastlane/fastlane-plugin-sapfire" 
         | 
| 43 | 
            +
            gem "fastlane-plugin-sapfire", git: "https://github.com/fastlane/fastlane-plugin-sapfire.git" 
         | 
| 40 44 | 
             
            ```
         | 
| 41 45 |  | 
| 42 46 | 
             
            ## Usage
         | 
| @@ -2,10 +2,15 @@ require "uri" | |
| 2 2 | 
             
            require "net/http"
         | 
| 3 3 | 
             
            require "json"
         | 
| 4 4 | 
             
            require "fastlane/action"
         | 
| 5 | 
            +
            require_relative "../helper/ms_credentials"
         | 
| 5 6 |  | 
| 6 7 | 
             
            module Fastlane
         | 
| 7 8 | 
             
              module Actions
         | 
| 8 9 | 
             
                class AssociateMsStoreAction < Action
         | 
| 10 | 
            +
                  @token = ""
         | 
| 11 | 
            +
                  @vsapi_host = ""
         | 
| 12 | 
            +
                  @vsapi_endpoint = ""
         | 
| 13 | 
            +
             | 
| 9 14 | 
             
                  XML_NAME = "Package.StoreAssociation.xml".freeze
         | 
| 10 15 |  | 
| 11 16 | 
             
                  def self.run(params)
         | 
| @@ -14,10 +19,11 @@ module Fastlane | |
| 14 19 | 
             
                    begin
         | 
| 15 20 | 
             
                      UI.message("Creating #{XML_NAME}...")
         | 
| 16 21 |  | 
| 17 | 
            -
                       | 
| 18 | 
            -
                       | 
| 19 | 
            -
                       | 
| 20 | 
            -
                       | 
| 22 | 
            +
                      acquire_authorization_token
         | 
| 23 | 
            +
                      acquire_vsapi_location
         | 
| 24 | 
            +
                      ms_developer_info = developer_info
         | 
| 25 | 
            +
                      ms_app_info = app_info(params[:app_id])
         | 
| 26 | 
            +
                      create_xml(params[:manifest], ms_developer_info, ms_app_info)
         | 
| 21 27 |  | 
| 22 28 | 
             
                      UI.message("#{XML_NAME} successfully created")
         | 
| 23 29 | 
             
                    rescue StandardError => ex
         | 
| @@ -26,50 +32,46 @@ module Fastlane | |
| 26 32 | 
             
                  end
         | 
| 27 33 |  | 
| 28 34 | 
             
                  def self.create_xml(manifest_path, developer_info, app_info)
         | 
| 29 | 
            -
                    app_identity = app_info[:identity]
         | 
| 30 | 
            -
                    app_names = app_info[:names]
         | 
| 31 | 
            -
                    publisher = developer_info[:publisher]
         | 
| 32 | 
            -
                    publisher_display_name = developer_info[:display_name]
         | 
| 33 35 | 
             
                    appxmanifest_xml = get_appxmanifest_xml(manifest_path)
         | 
| 34 36 |  | 
| 35 | 
            -
                    UI.message("Set identity name: #{ | 
| 37 | 
            +
                    UI.message("Set identity name: #{app_info.identity}")
         | 
| 36 38 |  | 
| 37 39 | 
             
                    begin
         | 
| 38 40 | 
             
                      identity_entry = appxmanifest_xml.elements["Package"]
         | 
| 39 41 | 
             
                                                       .elements["Identity"]
         | 
| 40 | 
            -
                      identity_entry.attributes["Name"] =  | 
| 41 | 
            -
                      identity_entry.attributes["Publisher"] = publisher
         | 
| 42 | 
            +
                      identity_entry.attributes["Name"] = app_info.identity
         | 
| 43 | 
            +
                      identity_entry.attributes["Publisher"] = developer_info.publisher
         | 
| 42 44 |  | 
| 43 45 | 
             
                      properties_entry = appxmanifest_xml.elements["Package"]
         | 
| 44 46 | 
             
                                                         .elements["Properties"]
         | 
| 45 | 
            -
                      properties_entry.elements["DisplayName"].text =  | 
| 46 | 
            -
                      properties_entry.elements["PublisherDisplayName"].text =  | 
| 47 | 
            +
                      properties_entry.elements["DisplayName"].text = app_info.names[0] unless app_info.names.empty?
         | 
| 48 | 
            +
                      properties_entry.elements["PublisherDisplayName"].text = developer_info.display_name
         | 
| 47 49 | 
             
                    rescue StandardError => ex
         | 
| 48 50 | 
             
                      UI.user_error!("Can't update app manifest: #{ex}")
         | 
| 49 51 | 
             
                    end
         | 
| 50 52 |  | 
| 51 53 | 
             
                    save_xml(appxmanifest_xml, manifest_path)
         | 
| 52 54 |  | 
| 53 | 
            -
                    UI.message("Set publisher data: #{publisher}")
         | 
| 54 | 
            -
                    UI.message("Set publisher display name: #{ | 
| 55 | 
            +
                    UI.message("Set publisher data: #{developer_info.publisher}")
         | 
| 56 | 
            +
                    UI.message("Set publisher display name: #{developer_info.display_name}")
         | 
| 55 57 |  | 
| 56 58 | 
             
                    document = REXML::Document.new
         | 
| 57 59 | 
             
                    document.xml_decl.version = "1.0"
         | 
| 58 60 | 
             
                    document.xml_decl.encoding = "utf-8"
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                    store_association = document.add_element("StoreAssociation", {
         | 
| 61 | 
            +
                    xmlns_args = {
         | 
| 61 62 | 
             
                      "xmlns" => "http://schemas.microsoft.com/appx/2010/storeassociation"
         | 
| 62 | 
            -
                    } | 
| 63 | 
            -
                    store_association.add_element(" | 
| 64 | 
            -
                    store_association.add_element(" | 
| 63 | 
            +
                    }
         | 
| 64 | 
            +
                    store_association = document.add_element("StoreAssociation", xmlns_args)
         | 
| 65 | 
            +
                    store_association.add_element("Publisher").text = developer_info.publisher
         | 
| 66 | 
            +
                    store_association.add_element("PublisherDisplayName").text = developer_info.display_name
         | 
| 65 67 | 
             
                    store_association.add_element("DeveloperAccountType").text = "WSA"
         | 
| 66 68 | 
             
                    store_association.add_element("GeneratePackageHash").text = "http://www.w3.org/2001/04/xmlenc#sha256"
         | 
| 67 69 |  | 
| 68 70 | 
             
                    product_reserved_info = store_association.add_element("ProductReservedInfo")
         | 
| 69 | 
            -
                    product_reserved_info.add_element("MainPackageIdentityName").text =  | 
| 71 | 
            +
                    product_reserved_info.add_element("MainPackageIdentityName").text = app_info.identity
         | 
| 70 72 |  | 
| 71 73 | 
             
                    reserved_names = product_reserved_info.add_element("ReservedNames")
         | 
| 72 | 
            -
                     | 
| 74 | 
            +
                    app_info.names.each do |x|
         | 
| 73 75 | 
             
                      reserved_names.add_element("ReservedName").text = x
         | 
| 74 76 | 
             
                    end
         | 
| 75 77 |  | 
| @@ -96,72 +98,61 @@ module Fastlane | |
| 96 98 | 
             
                    file.close
         | 
| 97 99 | 
             
                  end
         | 
| 98 100 |  | 
| 99 | 
            -
                  def self. | 
| 101 | 
            +
                  def self.developer_info
         | 
| 100 102 | 
             
                    UI.message("Obtaining developer info ...")
         | 
| 101 103 |  | 
| 102 104 | 
             
                    headers = {
         | 
| 103 | 
            -
                      "Authorization": "Bearer #{token}",
         | 
| 105 | 
            +
                      "Authorization": "Bearer #{@token}",
         | 
| 104 106 | 
             
                      "Accept": "application/json",
         | 
| 105 107 | 
             
                      "MS-Contract-Version": "1"
         | 
| 106 108 | 
             
                    }
         | 
| 107 109 | 
             
                    query = {
         | 
| 108 110 | 
             
                      setvar: "fltaad:1"
         | 
| 109 111 | 
             
                    }
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                    uri = URI("https://developer.microsoft.com/vsapi/developer")
         | 
| 112 | 
            -
                    uri.query = URI.encode_www_form(query)
         | 
| 113 | 
            -
                    https = Net::HTTP.new(uri.host, uri.port)
         | 
| 114 | 
            -
                    https.use_ssl = true
         | 
| 115 | 
            -
                    request = Net::HTTP::Get.new(uri, headers)
         | 
| 116 | 
            -
                    response = https.request(request)
         | 
| 112 | 
            +
                    connection = Faraday.new(@vsapi_host)
         | 
| 117 113 |  | 
| 118 114 | 
             
                    begin
         | 
| 119 | 
            -
                       | 
| 115 | 
            +
                      response = connection.get("#{@vsapi_endpoint}/developer", query, headers)
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                      if response.status == 200
         | 
| 118 | 
            +
                        data = JSON.parse(response.body)
         | 
| 119 | 
            +
                        developer_info = DeveloperInfo.new(data["PublisherDisplayName"], data["Publisher"])
         | 
| 120 120 |  | 
| 121 | 
            -
                      if response.code == "200"
         | 
| 122 121 | 
             
                        UI.message("Developer info was obtained")
         | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
                          publisher: data["Publisher"]
         | 
| 126 | 
            -
                        }
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                        return developer_info
         | 
| 127 124 | 
             
                      end
         | 
| 128 125 |  | 
| 129 | 
            -
                      UI.user_error!("Request returned the error: #{response. | 
| 126 | 
            +
                      UI.user_error!("Request returned the error: #{response.status}")
         | 
| 130 127 | 
             
                    rescue StandardError => ex
         | 
| 131 128 | 
             
                      UI.user_error!("Developer info request failed: #{ex}")
         | 
| 132 129 | 
             
                    end
         | 
| 133 130 | 
             
                  end
         | 
| 134 131 |  | 
| 135 | 
            -
                  def self. | 
| 132 | 
            +
                  def self.app_info(app_id)
         | 
| 136 133 | 
             
                    UI.message("Obtaining application info ...")
         | 
| 137 134 |  | 
| 138 135 | 
             
                    headers = {
         | 
| 139 | 
            -
                      "Authorization": "Bearer #{token}",
         | 
| 136 | 
            +
                      "Authorization": "Bearer #{@token}",
         | 
| 140 137 | 
             
                      "Accept": "application/json",
         | 
| 141 138 | 
             
                      "MS-Contract-Version": "1"
         | 
| 142 139 | 
             
                    }
         | 
| 143 140 | 
             
                    query = {
         | 
| 144 141 | 
             
                      setvar: "fltaad:1"
         | 
| 145 142 | 
             
                    }
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                    uri = URI("https://developer.microsoft.com/vsapi/applications")
         | 
| 148 | 
            -
                    uri.query = URI.encode_www_form(query)
         | 
| 149 | 
            -
                    https = Net::HTTP.new(uri.host, uri.port)
         | 
| 150 | 
            -
                    https.use_ssl = true
         | 
| 151 | 
            -
                    request = Net::HTTP::Get.new(uri, headers)
         | 
| 152 | 
            -
                    response = https.request(request)
         | 
| 143 | 
            +
                    connection = Faraday.new(@vsapi_host)
         | 
| 153 144 |  | 
| 154 145 | 
             
                    begin
         | 
| 155 | 
            -
                       | 
| 146 | 
            +
                      response = connection.get("#{@vsapi_endpoint}/applications", query, headers)
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                      if response.status == 200
         | 
| 149 | 
            +
                        data = JSON.parse(response.body)
         | 
| 150 | 
            +
                        product = data["Products"].find { |x| x["LandingUrl"].include?(app_id) }
         | 
| 151 | 
            +
                        app_info = AppInfo.new(product["MainPackageIdentityName"], product["ReservedNames"])
         | 
| 156 152 |  | 
| 157 | 
            -
                      if response.code == "200"
         | 
| 158 153 | 
             
                        UI.message("Application info was obtained")
         | 
| 159 154 |  | 
| 160 | 
            -
                         | 
| 161 | 
            -
                        return {
         | 
| 162 | 
            -
                          identity: product["MainPackageIdentityName"],
         | 
| 163 | 
            -
                          names: product["ReservedNames"]
         | 
| 164 | 
            -
                        }
         | 
| 155 | 
            +
                        return app_info
         | 
| 165 156 | 
             
                      end
         | 
| 166 157 |  | 
| 167 158 | 
             
                      UI.user_error!("Request returned the error: #{response.code}")
         | 
| @@ -173,40 +164,33 @@ module Fastlane | |
| 173 164 | 
             
                  def self.acquire_authorization_token
         | 
| 174 165 | 
             
                    UI.message("Acquiring authorization token ...")
         | 
| 175 166 |  | 
| 176 | 
            -
                     | 
| 177 | 
            -
                    password = Actions.lane_context[SharedValues::SF_MS_PASSWORD]
         | 
| 178 | 
            -
                    tenant_id = Actions.lane_context[SharedValues::SF_MS_TENANT_ID]
         | 
| 179 | 
            -
                    client_id = Actions.lane_context[SharedValues::SF_MS_CLIENT_ID]
         | 
| 180 | 
            -
                    client_secret = Actions.lane_context[SharedValues::SF_MS_CLIENT_SECRET]
         | 
| 181 | 
            -
             | 
| 167 | 
            +
                    ms_credentials = Helper.ms_credentials
         | 
| 182 168 | 
             
                    body = {
         | 
| 183 | 
            -
                      client_id: client_id,
         | 
| 184 | 
            -
                      client_secret: client_secret,
         | 
| 169 | 
            +
                      client_id: ms_credentials.client_id,
         | 
| 170 | 
            +
                      client_secret: ms_credentials.client_secret,
         | 
| 185 171 | 
             
                      client_info: 1,
         | 
| 186 172 | 
             
                      grant_type: "password",
         | 
| 187 | 
            -
                      scope: "https:// | 
| 188 | 
            -
                      username: username,
         | 
| 189 | 
            -
                      password: password
         | 
| 173 | 
            +
                      scope: "https://graph.windows.net/.default offline_access openid profile",
         | 
| 174 | 
            +
                      username: ms_credentials.username,
         | 
| 175 | 
            +
                      password: ms_credentials.password
         | 
| 190 176 | 
             
                    }
         | 
| 191 177 | 
             
                    headers = {
         | 
| 192 | 
            -
                      "x-anchormailbox": "upn:#{username}",
         | 
| 178 | 
            +
                      "x-anchormailbox": "upn:#{ms_credentials.username}",
         | 
| 193 179 | 
             
                      "x-client-sku": "fastlane-sapfire-plugin",
         | 
| 194 180 | 
             
                      "Accept": "application/json"
         | 
| 195 181 | 
             
                    }
         | 
| 196 | 
            -
             | 
| 197 | 
            -
                    uri = URI("https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token")
         | 
| 198 | 
            -
                    https = Net::HTTP.new(uri.host, uri.port)
         | 
| 199 | 
            -
                    https.use_ssl = true
         | 
| 200 | 
            -
                    request = Net::HTTP::Post.new(uri, headers)
         | 
| 182 | 
            +
                    connection = Faraday.new("https://login.microsoftonline.com")
         | 
| 201 183 | 
             
                    request_body = URI.encode_www_form(body)
         | 
| 202 | 
            -
                    response = https.request(request, request_body)
         | 
| 203 184 |  | 
| 204 185 | 
             
                    begin
         | 
| 186 | 
            +
                      response = connection.post("/#{ms_credentials.tenant_id}/oauth2/v2.0/token", request_body, headers)
         | 
| 205 187 | 
             
                      data = JSON.parse(response.body)
         | 
| 206 188 |  | 
| 207 | 
            -
                      if response. | 
| 189 | 
            +
                      if response.status == 200
         | 
| 190 | 
            +
                        @token = data["access_token"]
         | 
| 208 191 | 
             
                        UI.message("Authorization token was obtained")
         | 
| 209 | 
            -
             | 
| 192 | 
            +
             | 
| 193 | 
            +
                        return
         | 
| 210 194 | 
             
                      end
         | 
| 211 195 |  | 
| 212 196 | 
             
                      error = data["error"]
         | 
| @@ -218,6 +202,34 @@ module Fastlane | |
| 218 202 | 
             
                    end
         | 
| 219 203 | 
             
                  end
         | 
| 220 204 |  | 
| 205 | 
            +
                  def self.acquire_vsapi_location
         | 
| 206 | 
            +
                    query = {
         | 
| 207 | 
            +
                      LinkId: "875315" # VS API constant
         | 
| 208 | 
            +
                    }
         | 
| 209 | 
            +
                    connection = Faraday.new("https://go.microsoft.com")
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                    begin
         | 
| 212 | 
            +
                      response = connection.get("/fwlink", query)
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                      if response.status == 302
         | 
| 215 | 
            +
                        raise "'Location' header isn't presented" unless response.headers.include?("Location")
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                        location = response.headers["Location"]
         | 
| 218 | 
            +
                        uri = URI(location)
         | 
| 219 | 
            +
                        @vsapi_host = "#{uri.scheme}://#{uri.host}"
         | 
| 220 | 
            +
                        @vsapi_endpoint = uri.path
         | 
| 221 | 
            +
             | 
| 222 | 
            +
                        UI.message("VS API endpoint: #{location}")
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                        return
         | 
| 225 | 
            +
                      end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                      UI.user_error!("Request returned the error: #{response.status}")
         | 
| 228 | 
            +
                    rescue StandardError => ex
         | 
| 229 | 
            +
                      UI.user_error!("Failed to get VS API endpoint location: #{ex}")
         | 
| 230 | 
            +
                    end
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
             | 
| 221 233 | 
             
                  def self.description
         | 
| 222 234 | 
             
                    "Makes a local app manifest needed for Microsoft Store association"
         | 
| 223 235 | 
             
                  end
         | 
| @@ -268,5 +280,23 @@ module Fastlane | |
| 268 280 | 
             
                    :project
         | 
| 269 281 | 
             
                  end
         | 
| 270 282 | 
             
                end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                class DeveloperInfo
         | 
| 285 | 
            +
                  attr_reader :display_name, :publisher
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                  def initialize(display_name, publisher)
         | 
| 288 | 
            +
                    @display_name = display_name
         | 
| 289 | 
            +
                    @publisher = publisher
         | 
| 290 | 
            +
                  end
         | 
| 291 | 
            +
                end
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                class AppInfo
         | 
| 294 | 
            +
                  attr_reader :identity, :names
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                  def initialize(identity, names)
         | 
| 297 | 
            +
                    @identity = identity
         | 
| 298 | 
            +
                    @names = names
         | 
| 299 | 
            +
                  end
         | 
| 300 | 
            +
                end
         | 
| 271 301 | 
             
              end
         | 
| 272 302 | 
             
            end
         | 
| @@ -1,22 +1,19 @@ | |
| 1 1 | 
             
            require "fastlane/action"
         | 
| 2 | 
            +
            require_relative "../helper/ms_credentials"
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Fastlane
         | 
| 4 5 | 
             
              module Actions
         | 
| 5 6 | 
             
                module SharedValues
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
                  SF_MS_PASSWORD = :SF_MS_PASSWORD
         | 
| 8 | 
            -
                  SF_MS_TENANT_ID = :SF_MS_TENANT_ID
         | 
| 9 | 
            -
                  SF_MS_CLIENT_ID = :SF_MS_CLIENT_ID
         | 
| 10 | 
            -
                  SF_MS_CLIENT_SECRET = :SF_MS_CLIENT_SECRET
         | 
| 7 | 
            +
                  SF_MS_CREDENTIALS = :SF_MS_CREDENTIALS
         | 
| 11 8 | 
             
                end
         | 
| 12 9 |  | 
| 13 10 | 
             
                class MsCredentialsAction < Action
         | 
| 14 11 | 
             
                  def self.run(params)
         | 
| 15 | 
            -
                     | 
| 16 | 
            -
                     | 
| 17 | 
            -
                     | 
| 18 | 
            -
                     | 
| 19 | 
            -
                     | 
| 12 | 
            +
                    Helper.ms_credentials.username = params[:username]
         | 
| 13 | 
            +
                    Helper.ms_credentials.password = params[:password]
         | 
| 14 | 
            +
                    Helper.ms_credentials.tenant_id = params[:tenant_id]
         | 
| 15 | 
            +
                    Helper.ms_credentials.client_id = params[:client_id]
         | 
| 16 | 
            +
                    Helper.ms_credentials.client_secret = params[:client_secret]
         | 
| 20 17 |  | 
| 21 18 | 
             
                    UI.success("Credentials for Azure AD account is successfully saved for further actions")
         | 
| 22 19 | 
             
                  end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require "fastlane/action"
         | 
| 2 | 
            +
            require_relative "../helper/ms_credentials"
         | 
| 2 3 | 
             
            require_relative "../helper/ms_devcenter_helper"
         | 
| 3 4 | 
             
            require_relative "../msbuild/options"
         | 
| 4 5 |  | 
| @@ -8,18 +9,37 @@ module Fastlane | |
| 8 9 | 
             
                  DEFAULT_TIMEOUT = 300
         | 
| 9 10 |  | 
| 10 11 | 
             
                  def self.run(params)
         | 
| 11 | 
            -
                     | 
| 12 | 
            -
                    client_id = Actions.lane_context[SharedValues::SF_MS_CLIENT_ID]
         | 
| 13 | 
            -
                    client_secret = Actions.lane_context[SharedValues::SF_MS_CLIENT_SECRET]
         | 
| 12 | 
            +
                    ms_credentials = Helper.ms_credentials
         | 
| 14 13 | 
             
                    app_id = params[:app_id]
         | 
| 15 14 | 
             
                    path = params[:path]
         | 
| 16 15 | 
             
                    timeout = params.values.include?(:timeout) && params[:timeout].positive? ? params[:timeout] : DEFAULT_TIMEOUT
         | 
| 17 16 |  | 
| 18 17 | 
             
                    UI.message("Acquiring authorization token for DevCenter ...")
         | 
| 19 | 
            -
                    auth_token = Helper::MsDevCenterHelper.acquire_authorization_token(tenant_id, | 
| 18 | 
            +
                    auth_token = Helper::MsDevCenterHelper.acquire_authorization_token(ms_credentials.tenant_id,
         | 
| 19 | 
            +
                                                                                       ms_credentials.client_id,
         | 
| 20 | 
            +
                                                                                       ms_credentials.client_secret,
         | 
| 21 | 
            +
                                                                                       timeout)
         | 
| 20 22 | 
             
                    UI.message("Authorization token was obtained")
         | 
| 21 23 |  | 
| 22 24 | 
             
                    UI.message("Creating submission for app #{app_id} ...")
         | 
| 25 | 
            +
                    pending_submission = Helper::MsDevCenterHelper.non_published_submission(app_id, auth_token, timeout)
         | 
| 26 | 
            +
                    submission_id = pending_submission["id"]
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    unless pending_submission.nil?
         | 
| 29 | 
            +
                      if params.values.include?(:remove_pending_submission) &&
         | 
| 30 | 
            +
                         [true].include?(params[:remove_pending_submission])
         | 
| 31 | 
            +
                        UI.message("Pending submission #{submission_id} were found and scheduled for deletion due to 'remove_pending_submission' argument set to 'true'")
         | 
| 32 | 
            +
                        Helper::MsDevCenterHelper.remove_submission(app_id, submission_id, auth_token, timeout)
         | 
| 33 | 
            +
                        UI.message("Pending submission deleted")
         | 
| 34 | 
            +
                      else
         | 
| 35 | 
            +
                        UI.user_error!([
         | 
| 36 | 
            +
                          "There is a pending submission #{submission_id} has already been created.",
         | 
| 37 | 
            +
                          "You need to either proceed it or remove before creating a new one.",
         | 
| 38 | 
            +
                          "Set 'remove_pending_submission' argument to 'true' to do that automatically."
         | 
| 39 | 
            +
                        ].join(" "))
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 23 43 | 
             
                    submission_obj = Helper::MsDevCenterHelper.create_submission(app_id, auth_token, timeout)
         | 
| 24 44 | 
             
                    submission_id = submission_obj["id"]
         | 
| 25 45 | 
             
                    UI.message("Submission #{submission_id} created")
         | 
| @@ -46,7 +66,6 @@ module Fastlane | |
| 46 66 | 
             
                       [true].include?(params[:skip_waiting_for_build_processing])
         | 
| 47 67 | 
             
                      UI.success("Submission passed, but build processing were skipped. Check the Dev Center page.")
         | 
| 48 68 | 
             
                      return
         | 
| 49 | 
            -
             | 
| 50 69 | 
             
                    end
         | 
| 51 70 |  | 
| 52 71 | 
             
                    status = false
         | 
| @@ -127,6 +146,13 @@ module Fastlane | |
| 127 146 | 
             
                        default_value: false,
         | 
| 128 147 | 
             
                        type: Fastlane::Boolean
         | 
| 129 148 | 
             
                      ),
         | 
| 149 | 
            +
                      FastlaneCore::ConfigItem.new(
         | 
| 150 | 
            +
                        key: :remove_pending_submission,
         | 
| 151 | 
            +
                        description: "If set to true, the pending submission halts - a new one will be created automatically",
         | 
| 152 | 
            +
                        optional: true,
         | 
| 153 | 
            +
                        default_value: false,
         | 
| 154 | 
            +
                        type: Fastlane::Boolean
         | 
| 155 | 
            +
                      ),
         | 
| 130 156 | 
             
                      FastlaneCore::ConfigItem.new(
         | 
| 131 157 | 
             
                        key: :path,
         | 
| 132 158 | 
             
                        env_name: "SF_PACKAGE",
         | 
| @@ -156,8 +182,8 @@ module Fastlane | |
| 156 182 | 
             
                  end
         | 
| 157 183 |  | 
| 158 184 | 
             
                  def self.create_blob_zip(package_path)
         | 
| 159 | 
            -
                    File.delete(File.join(File.dirname(package_path), "blob.zip"))
         | 
| 160 185 | 
             
                    zip_path = File.join(File.dirname(package_path), "blob.zip")
         | 
| 186 | 
            +
                    File.delete(zip_path) if File.exist?(zip_path)
         | 
| 161 187 |  | 
| 162 188 | 
             
                    Zip::File.open(zip_path, create: true) do |file|
         | 
| 163 189 | 
             
                      file.add(File.basename(package_path), package_path)
         | 
| @@ -181,7 +207,14 @@ module Fastlane | |
| 181 207 | 
             
                      "minimumSystemRam": "None"
         | 
| 182 208 | 
             
                    }
         | 
| 183 209 |  | 
| 184 | 
            -
                     | 
| 210 | 
            +
                    if submission_obj[key].empty?
         | 
| 211 | 
            +
                      submission_obj[key] = []
         | 
| 212 | 
            +
                    else
         | 
| 213 | 
            +
                      submission_obj[key].each do |existing_package|
         | 
| 214 | 
            +
                        existing_package["fileStatus"] = "PendingDelete"
         | 
| 215 | 
            +
                      end
         | 
| 216 | 
            +
                    end
         | 
| 217 | 
            +
             | 
| 185 218 | 
             
                    submission_obj[key].append(package)
         | 
| 186 219 | 
             
                    submission_obj
         | 
| 187 220 | 
             
                  end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module Fastlane
         | 
| 2 | 
            +
              module Helper
         | 
| 3 | 
            +
                class MsCredentials
         | 
| 4 | 
            +
                  attr_writer :username, :password, :tenant_id, :client_id, :client_secret
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def username
         | 
| 7 | 
            +
                    check_value("username", @username)
         | 
| 8 | 
            +
                    @username
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def password
         | 
| 12 | 
            +
                    check_value("password", @password)
         | 
| 13 | 
            +
                    @password
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def tenant_id
         | 
| 17 | 
            +
                    check_value("tenant_id", @tenant_id)
         | 
| 18 | 
            +
                    @tenant_id
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def client_id
         | 
| 22 | 
            +
                    check_value("client_id", @client_id)
         | 
| 23 | 
            +
                    @client_id
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def client_secret
         | 
| 27 | 
            +
                    check_value("client_secret", @client_secret)
         | 
| 28 | 
            +
                    @client_secret
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def check_value(name, value)
         | 
| 32 | 
            +
                    raise "Microsoft credential variable hasn't been set: #{name}. You must call ms_credentials action before." if
         | 
| 33 | 
            +
                      value.nil? || value.empty?
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  private(:check_value)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                class << self
         | 
| 40 | 
            +
                  attr_accessor :ms_credentials
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                self.ms_credentials = MsCredentials.new
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                private_class_method(:ms_credentials=)
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -53,19 +53,24 @@ module Fastlane | |
| 53 53 | 
             
                    url_data = parse_upload_url(url)
         | 
| 54 54 | 
             
                    url_data[:query]["comp"] = "block"
         | 
| 55 55 | 
             
                    url_data[:query]["blockid"] = id
         | 
| 56 | 
            -
             | 
| 57 56 | 
             
                    connection = Faraday.new(url_data[:host])
         | 
| 58 | 
            -
                    response = connection.put(url_data[:path]) do |req|
         | 
| 59 | 
            -
                      req.headers = headers
         | 
| 60 | 
            -
                      req.params = url_data[:query]
         | 
| 61 | 
            -
                      req.body = bytes
         | 
| 62 | 
            -
                      req.options.timeout = timeout if timeout.positive?
         | 
| 63 | 
            -
                    end
         | 
| 64 57 |  | 
| 65 | 
            -
                     | 
| 58 | 
            +
                    begin
         | 
| 59 | 
            +
                      response = connection.put(url_data[:path]) do |req|
         | 
| 60 | 
            +
                        req.headers = headers
         | 
| 61 | 
            +
                        req.params = url_data[:query]
         | 
| 62 | 
            +
                        req.body = bytes
         | 
| 63 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      return true if response.status == 201
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      error = response.body.to_s
         | 
| 69 | 
            +
                      UI.error("Upload request failed.\nCode: #{response.status}\nError: #{error}")
         | 
| 70 | 
            +
                    rescue StandardError => ex
         | 
| 71 | 
            +
                      UI.error("Upload request failed: #{ex}")
         | 
| 72 | 
            +
                    end
         | 
| 66 73 |  | 
| 67 | 
            -
                    error = response.body.to_s
         | 
| 68 | 
            -
                    UI.error("Upload request failed.\nCode: #{response.status}\nError: #{error}")
         | 
| 69 74 | 
             
                    false
         | 
| 70 75 | 
             
                  end
         | 
| 71 76 |  | 
| @@ -81,18 +86,23 @@ module Fastlane | |
| 81 86 |  | 
| 82 87 | 
             
                    url_data = parse_upload_url(url)
         | 
| 83 88 | 
             
                    url_data[:query]["comp"] = "blocklist"
         | 
| 84 | 
            -
             | 
| 85 89 | 
             
                    connection = Faraday.new(url_data[:host])
         | 
| 86 | 
            -
                    response = connection.put(url_data[:path]) do |req|
         | 
| 87 | 
            -
                      req.params = url_data[:query]
         | 
| 88 | 
            -
                      req.body = document.to_s
         | 
| 89 | 
            -
                      req.options.timeout = timeout if timeout.positive?
         | 
| 90 | 
            -
                    end
         | 
| 91 90 |  | 
| 92 | 
            -
                     | 
| 91 | 
            +
                    begin
         | 
| 92 | 
            +
                      response = connection.put(url_data[:path]) do |req|
         | 
| 93 | 
            +
                        req.params = url_data[:query]
         | 
| 94 | 
            +
                        req.body = document.to_s
         | 
| 95 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      return true if response.status == 201
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                      error = response.body.to_s
         | 
| 101 | 
            +
                      UI.error("Upload block list request failed.\nCode: #{response.status}\nError: #{error}")
         | 
| 102 | 
            +
                    rescue StandardError => ex
         | 
| 103 | 
            +
                      UI.error("Upload block list request failed: #{ex}")
         | 
| 104 | 
            +
                    end
         | 
| 93 105 |  | 
| 94 | 
            -
                    error = response.body.to_s
         | 
| 95 | 
            -
                    UI.error("Upload block list request failed.\nCode: #{response.status}\nError: #{error}")
         | 
| 96 106 | 
             
                    false
         | 
| 97 107 | 
             
                  end
         | 
| 98 108 |  | 
| @@ -100,16 +110,17 @@ module Fastlane | |
| 100 110 | 
             
                    check_app_id(app_id)
         | 
| 101 111 |  | 
| 102 112 | 
             
                    connection = Faraday.new(HOST)
         | 
| 103 | 
            -
                    response = connection.get("/#{API_VERSION}/#{API_ROOT}/#{app_id}") do |req|
         | 
| 104 | 
            -
                      req.headers = build_headers(auth_token)
         | 
| 105 | 
            -
                      req.options.timeout = timeout if timeout.positive?
         | 
| 106 | 
            -
                    end
         | 
| 107 113 |  | 
| 108 114 | 
             
                    begin
         | 
| 115 | 
            +
                      response = connection.get("/#{API_VERSION}/#{API_ROOT}/#{app_id}") do |req|
         | 
| 116 | 
            +
                        req.headers = build_headers(auth_token)
         | 
| 117 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 118 | 
            +
                      end
         | 
| 109 119 | 
             
                      data = JSON.parse(response.body)
         | 
| 120 | 
            +
             | 
| 110 121 | 
             
                      return data if response.status == 200
         | 
| 111 122 |  | 
| 112 | 
            -
                      UI.user_error!(" | 
| 123 | 
            +
                      UI.user_error!("Getting app request returned the error.\nCode: #{response.status}")
         | 
| 113 124 | 
             
                    rescue StandardError => ex
         | 
| 114 125 | 
             
                      UI.user_error!("Getting app info process failed: #{ex}")
         | 
| 115 126 | 
             
                    end
         | 
| @@ -117,27 +128,22 @@ module Fastlane | |
| 117 128 |  | 
| 118 129 | 
             
                  def self.create_submission(app_id, auth_token, timeout = 0)
         | 
| 119 130 | 
             
                    check_app_id(app_id)
         | 
| 120 | 
            -
                    if non_published_submission?(app_id, auth_token, timeout)
         | 
| 121 | 
            -
                      UI.user_error!([
         | 
| 122 | 
            -
                        "There is a pending submission has already been created.",
         | 
| 123 | 
            -
                        "You need to either proceed it or remove before creating a new one."
         | 
| 124 | 
            -
                      ].join(" "))
         | 
| 125 | 
            -
                    end
         | 
| 126 131 |  | 
| 127 132 | 
             
                    connection = Faraday.new(HOST)
         | 
| 128 | 
            -
                    response = connection.post("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions") do |req|
         | 
| 129 | 
            -
                      req.headers = build_headers(auth_token)
         | 
| 130 | 
            -
                      req.options.timeout = timeout if timeout.positive?
         | 
| 131 | 
            -
                    end
         | 
| 132 133 |  | 
| 133 134 | 
             
                    begin
         | 
| 135 | 
            +
                      response = connection.post("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions") do |req|
         | 
| 136 | 
            +
                        req.headers = build_headers(auth_token)
         | 
| 137 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 138 | 
            +
                      end
         | 
| 134 139 | 
             
                      data = JSON.parse(response.body)
         | 
| 140 | 
            +
             | 
| 135 141 | 
             
                      return data if response.status == 201
         | 
| 136 142 |  | 
| 137 143 | 
             
                      code = data["code"]
         | 
| 138 144 | 
             
                      message = data["message"]
         | 
| 139 145 |  | 
| 140 | 
            -
                      UI.user_error!(" | 
| 146 | 
            +
                      UI.user_error!("Creating submission request returned the error.\nCode: #{response.status} #{code}.\nDescription: #{message}")
         | 
| 141 147 | 
             
                    rescue StandardError => ex
         | 
| 142 148 | 
             
                      UI.user_error!("Creating submission process failed: #{ex}")
         | 
| 143 149 | 
             
                    end
         | 
| @@ -149,17 +155,18 @@ module Fastlane | |
| 149 155 |  | 
| 150 156 | 
             
                    submission_id = submission_obj["id"]
         | 
| 151 157 | 
             
                    connection = Faraday.new(HOST)
         | 
| 152 | 
            -
                    response = connection.put("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions/#{submission_id}") do |req|
         | 
| 153 | 
            -
                      req.headers = build_headers(auth_token)
         | 
| 154 | 
            -
                      req.body = submission_obj.to_json
         | 
| 155 | 
            -
                      req.options.timeout = timeout if timeout.positive?
         | 
| 156 | 
            -
                    end
         | 
| 157 158 |  | 
| 158 159 | 
             
                    begin
         | 
| 160 | 
            +
                      response = connection.put("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions/#{submission_id}") do |req|
         | 
| 161 | 
            +
                        req.headers = build_headers(auth_token)
         | 
| 162 | 
            +
                        req.body = submission_obj.to_json
         | 
| 163 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 164 | 
            +
                      end
         | 
| 159 165 | 
             
                      data = JSON.parse(response.body)
         | 
| 166 | 
            +
             | 
| 160 167 | 
             
                      return data if response.status == 200
         | 
| 161 168 |  | 
| 162 | 
            -
                      UI.user_error!(" | 
| 169 | 
            +
                      UI.user_error!("Updating submission request returned the error.\nCode: #{response.status}")
         | 
| 163 170 | 
             
                    rescue StandardError => ex
         | 
| 164 171 | 
             
                      UI.user_error!("Updating submission process failed: #{ex}")
         | 
| 165 172 | 
             
                    end
         | 
| @@ -170,29 +177,75 @@ module Fastlane | |
| 170 177 | 
             
                    check_submission_id(submission_id)
         | 
| 171 178 |  | 
| 172 179 | 
             
                    connection = Faraday.new(HOST)
         | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
                       | 
| 180 | 
            +
             | 
| 181 | 
            +
                    begin
         | 
| 182 | 
            +
                      response = connection.post("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions/#{submission_id}/commit") do |req|
         | 
| 183 | 
            +
                        req.headers = build_headers(auth_token)
         | 
| 184 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 185 | 
            +
                      end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                      UI.user_error!("Committing submission request returned the error.\nCode: #{response.status}") unless response.status == 202
         | 
| 188 | 
            +
                    rescue StandardError => ex
         | 
| 189 | 
            +
                      UI.user_error!("Committing submission process failed: #{ex}")
         | 
| 176 190 | 
             
                    end
         | 
| 191 | 
            +
                  end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                  def self.remove_submission(app_id, submission_id, auth_token, timeout = 0)
         | 
| 194 | 
            +
                    check_app_id(app_id)
         | 
| 195 | 
            +
                    check_submission_id(submission_id)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                    connection = Faraday.new(HOST)
         | 
| 177 198 |  | 
| 178 | 
            -
                     | 
| 199 | 
            +
                    begin
         | 
| 200 | 
            +
                      response = connection.delete("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions/#{submission_id}") do |req|
         | 
| 201 | 
            +
                        req.headers = build_headers(auth_token)
         | 
| 202 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 203 | 
            +
                      end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                      UI.user_error!("Deleting submission request returned the error.\nCode: #{response.status}") unless response.status == 204
         | 
| 206 | 
            +
                    rescue StandardError => ex
         | 
| 207 | 
            +
                      UI.user_error!("Deleting submission process failed: #{ex}")
         | 
| 208 | 
            +
                    end
         | 
| 179 209 | 
             
                  end
         | 
| 180 210 |  | 
| 181 211 | 
             
                  def self.get_submission_status(app_id, submission_id, auth_token, timeout = 0)
         | 
| 182 212 | 
             
                    check_app_id(app_id)
         | 
| 183 213 | 
             
                    check_submission_id(submission_id)
         | 
| 184 214 |  | 
| 185 | 
            -
                     | 
| 186 | 
            -
             | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 215 | 
            +
                    response = get_submission_status_internal(app_id, submission_id, auth_token, timeout)
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    # Sometimes MS can return internal server error code (500) that is not directly related to uploading process.
         | 
| 218 | 
            +
                    # Once it happens, retry 3 times until we'll get a success response.
         | 
| 219 | 
            +
                    if response[:status] == 500
         | 
| 220 | 
            +
                      server_error_500_retry_counter = 0
         | 
| 221 | 
            +
             | 
| 222 | 
            +
                      until server_error_500_retry_counter < 2
         | 
| 223 | 
            +
                        server_error_500_retry_counter += 1
         | 
| 224 | 
            +
                        response = get_submission_status_internal(app_id, submission_id, auth_token, timeout)
         | 
| 225 | 
            +
                        break if response.nil? || response[:status] == 200
         | 
| 226 | 
            +
                      end
         | 
| 189 227 | 
             
                    end
         | 
| 190 228 |  | 
| 229 | 
            +
                    return response[:data] if !response.nil? && response[:status] == 200
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    UI.user_error!("Submission status obtaining request returned the error.\nCode: #{response[:status]}")
         | 
| 232 | 
            +
                  end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                  def self.get_submission_status_internal(app_id, submission_id, auth_token, timeout = 0)
         | 
| 235 | 
            +
                    connection = Faraday.new(HOST)
         | 
| 236 | 
            +
             | 
| 191 237 | 
             
                    begin
         | 
| 192 | 
            -
                       | 
| 193 | 
            -
             | 
| 238 | 
            +
                      response = connection.get("/#{API_VERSION}/#{API_ROOT}/#{app_id}/submissions/#{submission_id}/status") do |req|
         | 
| 239 | 
            +
                        req.headers = build_headers(auth_token)
         | 
| 240 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 241 | 
            +
                      end
         | 
| 194 242 |  | 
| 195 | 
            -
                      UI. | 
| 243 | 
            +
                      UI.error("Submission status obtaining request returned the error.\nCode: #{response.status}") if response.status != 200
         | 
| 244 | 
            +
                      data = response.status == 200 ? JSON.parse(response.body) : nil
         | 
| 245 | 
            +
                      {
         | 
| 246 | 
            +
                        "data": data,
         | 
| 247 | 
            +
                        "status": response.status
         | 
| 248 | 
            +
                      }
         | 
| 196 249 | 
             
                    rescue StandardError => ex
         | 
| 197 250 | 
             
                      UI.user_error!("Submission status obtaining process failed: #{ex}")
         | 
| 198 251 | 
             
                    end
         | 
| @@ -208,31 +261,31 @@ module Fastlane | |
| 208 261 | 
             
                    headers = {
         | 
| 209 262 | 
             
                      "Content-Type": "application/x-www-form-urlencoded"
         | 
| 210 263 | 
             
                    }.merge(REQUEST_HEADERS)
         | 
| 211 | 
            -
             | 
| 212 264 | 
             
                    connection = Faraday.new("https://login.microsoftonline.com")
         | 
| 213 | 
            -
                    response = connection.post("/#{tenant_id}/oauth2/token") do |req|
         | 
| 214 | 
            -
                      req.headers = headers
         | 
| 215 | 
            -
                      req.body = body
         | 
| 216 | 
            -
                      req.options.timeout = timeout if timeout.positive?
         | 
| 217 | 
            -
                    end
         | 
| 218 265 |  | 
| 219 266 | 
             
                    begin
         | 
| 267 | 
            +
                      response = connection.post("/#{tenant_id}/oauth2/token") do |req|
         | 
| 268 | 
            +
                        req.headers = headers
         | 
| 269 | 
            +
                        req.body = body
         | 
| 270 | 
            +
                        req.options.timeout = timeout if timeout.positive?
         | 
| 271 | 
            +
                      end
         | 
| 220 272 | 
             
                      data = JSON.parse(response.body)
         | 
| 273 | 
            +
             | 
| 221 274 | 
             
                      return data["access_token"] if response.status == 200
         | 
| 222 275 |  | 
| 223 276 | 
             
                      error = data["error"]
         | 
| 224 277 | 
             
                      error_description = data["error_description"]
         | 
| 225 278 |  | 
| 226 | 
            -
                      UI.user_error!(" | 
| 279 | 
            +
                      UI.user_error!("Authorization request returned the error.\nCode: #{error}.\nDescription: #{error_description}")
         | 
| 227 280 | 
             
                    rescue StandardError => ex
         | 
| 228 281 | 
             
                      UI.user_error!("Authorization failed: #{ex}")
         | 
| 229 282 | 
             
                    end
         | 
| 230 283 | 
             
                  end
         | 
| 231 284 |  | 
| 232 | 
            -
                  def self.non_published_submission | 
| 285 | 
            +
                  def self.non_published_submission(app_id, auth_token, timeout = 0)
         | 
| 233 286 | 
             
                    check_app_id(app_id)
         | 
| 234 287 | 
             
                    app_info = get_app_info(app_id, auth_token, timeout)
         | 
| 235 | 
            -
                    app_info | 
| 288 | 
            +
                    app_info["pendingApplicationSubmission"]
         | 
| 236 289 | 
             
                  end
         | 
| 237 290 |  | 
| 238 291 | 
             
                  def self.build_headers(auth_token)
         | 
| @@ -270,16 +323,18 @@ module Fastlane | |
| 270 323 | 
             
                  public_class_method(:create_submission)
         | 
| 271 324 | 
             
                  public_class_method(:update_submission)
         | 
| 272 325 | 
             
                  public_class_method(:commit_submission)
         | 
| 326 | 
            +
                  public_class_method(:remove_submission)
         | 
| 273 327 | 
             
                  public_class_method(:get_submission_status)
         | 
| 274 328 | 
             
                  public_class_method(:acquire_authorization_token)
         | 
| 329 | 
            +
                  public_class_method(:non_published_submission)
         | 
| 275 330 |  | 
| 276 331 | 
             
                  private_class_method(:upload_block)
         | 
| 277 332 | 
             
                  private_class_method(:upload_block_list)
         | 
| 278 | 
            -
                  private_class_method(:non_published_submission?)
         | 
| 279 333 | 
             
                  private_class_method(:build_headers)
         | 
| 280 334 | 
             
                  private_class_method(:parse_upload_url)
         | 
| 281 335 | 
             
                  private_class_method(:check_app_id)
         | 
| 282 336 | 
             
                  private_class_method(:check_submission_id)
         | 
| 337 | 
            +
                  private_class_method(:get_submission_status_internal)
         | 
| 283 338 |  | 
| 284 339 | 
             
                  private_constant(:HOST)
         | 
| 285 340 | 
             
                  private_constant(:API_VERSION)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: fastlane-plugin-sapfire
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - CheeryLee
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2024-03-24 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: faraday
         | 
| @@ -145,6 +145,7 @@ files: | |
| 145 145 | 
             
            - lib/fastlane/plugin/sapfire/actions/upload_ms_store_action.rb
         | 
| 146 146 | 
             
            - lib/fastlane/plugin/sapfire/actions/upload_nuget_action.rb
         | 
| 147 147 | 
             
            - lib/fastlane/plugin/sapfire/actions_base/msbuild_action_base.rb
         | 
| 148 | 
            +
            - lib/fastlane/plugin/sapfire/helper/ms_credentials.rb
         | 
| 148 149 | 
             
            - lib/fastlane/plugin/sapfire/helper/ms_devcenter_helper.rb
         | 
| 149 150 | 
             
            - lib/fastlane/plugin/sapfire/helper/sapfire_helper.rb
         | 
| 150 151 | 
             
            - lib/fastlane/plugin/sapfire/msbuild/module.rb
         |