Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for files.getUploadURLExternal and files.completeUploadExternal #176

Open
wants to merge 30 commits into
base: main
Choose a base branch
from

Conversation

maybe-Baylie
Copy link

This pull request introduces support for two new endpoints:

  1. files.getUploadURLExternal docs
  2. files.completeUploadExternal docs

Additionally, A few errors were corrected in the command classes to allow the tools to run

@maybe-Baylie
Copy link
Author

I can add the string literal to class string conversions from Jane as well if desired, will add 448 file changes in generated/normalizer/.

@@ -16828,6 +16828,130 @@
]
}
},
"/files.completeUploadExternal": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proper way to add stuffs in this file is described here: https://github.com/jolicode/slack-php-api/blob/main/docs/4-updating-sdk.md#generating-a-new-patch

You need to run a command to update the .patch.

Copy link
Author

@maybe-Baylie maybe-Baylie Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rebased the branch and followed the instructions in the section and confirmed the patch was correctly applied. Should I write unit tests in the WritingTest.php file?

*edit: I think I answered my own question for the test

@damienalexandre
Copy link
Member

Also, can you rebase your PR? main has been updated.

@maybe-Baylie
Copy link
Author

@damienalexandre Is there a preferred way for sending a post request with the client that isn't to a normal endpoint? To complete the test for FilesCompleteUploadExternal, I need to post the file to the link returned from filesGetUploadURLExternal. I would like to use the client since the auth is already attached to it but I'm not super familiar with how I would go about that. Mocking an endpoint maybe?

@damienalexandre
Copy link
Member

Actually, if we must POST to a custom URL, we should create a new customizable Endpoint in the lib, inspired by the existing upload one (https://github.com/jolicode/slack-php-api/blob/main/generated/Endpoint/FilesUpload.php).

class NewFileUpload extends \JoliCode\Slack\Api\Runtime\Client\BaseEndpoint implements \JoliCode\Slack\Api\Runtime\Client\Endpoint
{
    use \JoliCode\Slack\Api\Runtime\Client\EndpointTrait;
   
    private $uri;

    public function __construct(string $uri, array $queryParameters = [], array $headerParameters = [])
    {
        $this->queryParameters = $queryParameters;
        $this->headerParameters = $headerParameters;
    }

    public function getMethod(): string
    {
        return 'POST';
    }

    public function getUri(): string
    {
        return $this->uri;
    }

    public function getBody(\Symfony\Component\Serializer\SerializerInterface $serializer, $streamFactory = null): array
    {
        return $this->getMultipartBody($streamFactory);
    }

    public function getExtraHeaders(): array
    {
        return ['Accept' => ['application/json']];
    }

    public function getAuthenticationScopes(): array
    {
        return ['slackAuth'];
    }

    protected function getQueryOptionsResolver(): \Symfony\Component\OptionsResolver\OptionsResolver
    {
        $optionsResolver = parent::getQueryOptionsResolver();

        // todo

        return $optionsResolver;
    }

    protected function getHeadersOptionsResolver(): \Symfony\Component\OptionsResolver\OptionsResolver
    {
        $optionsResolver = parent::getHeadersOptionsResolver();
        // todo

        return $optionsResolver;
    }

    protected function transformResponseBody(\Psr\Http\Message\ResponseInterface $response, \Symfony\Component\Serializer\SerializerInterface $serializer, ?string $contentType = null)
    {
        // todo
    }
}

Then it can be used like this in the Client:

    public function newfileUpload(string $uploadUri, array $formParameters = [], string $fetch = self::FETCH_OBJECT)
    {
        return $this->executeEndpoint(new Endpoint\NewFileUpload($uploadUri, $formParameters), $fetch);
    }

Would that be an option? Do you think it's the way to go? I didn't study the new upload behavior yet but if you can to POST to a custom URI, that's the way to go I think.

@maybe-Baylie
Copy link
Author

That seems like a fine approach to me. Based on slacks own implementation in the WebAPI, they ended up defining the filesUploadV2 method as a convenience wrapper around the two new endpoints with an internal post call in between. I'll get started on it and see what I can do

…nse200.php, change files arg to string for FilesCompleteUploadExternal.php, remove bad test
@maybe-Baylie
Copy link
Author

I was able to validate that the methods worked by using a guzzle client to make the actual post request in a separate project. As it currently stands, this PR has the necessary changes to use the two new methods for completing a file upload.

I have been trying to debug the custom endpoint to make the Post Request and I'm continuing to get this generic error message.

image

I stepped through it and it appears to be a timeout issue, but I'm a little lost with trying to overcome it. The request URL appears to be properly formatted

Im going to be leaving for vacation at 5pm est today so I'm not sure exactly how you'd like me to proceed. I can commit the code I have been trying to work to this branch or I can share it else where. Im happy to keep trying until 5.

Here is my current implementation of the new class. The types in transformResponseBody() are just direct copies of the types for FilesUpload, but I haven't gotten far enough to know if they need to be customized or not.

class FileUploadToURLExternal extends \JoliCode\Slack\Api\Runtime\Client\BaseEndpoint implements \JoliCode\Slack\Api\Runtime\Client\Endpoint
{
    use \JoliCode\Slack\Api\Runtime\Client\EndpointTrait;

    private $uri;

    public function __construct(string $uri, array $queryParameters = [], array $headerParameters = [])
    {
        $this->uri = $uri;

        $this->queryParameters = $queryParameters;
        $this->headerParameters = $headerParameters;
    }

    public function getMethod(): string
    {
        return 'POST';
    }

    public function getUri(): string
    {
        return $this->uri;
    }

    public function getBody(\Symfony\Component\Serializer\SerializerInterface $serializer, $streamFactory = null): array
    {
        return $this->getMultipartBody($streamFactory);
    }

    public function getExtraHeaders(): array
    {
        return ['Accept' => ['application/json']];
    }

    public function getAuthenticationScopes(): array
    {
        return ['slackAuth'];
    }

    protected function getQueryOptionsResolver(): \Symfony\Component\OptionsResolver\OptionsResolver
    {
        $optionsResolver = parent::getQueryOptionsResolver();
        $optionsResolver->setDefined(['filename', 'content', 'file']);
        $optionsResolver->setRequired([]);
        $optionsResolver->setDefaults([]);
        $optionsResolver->addAllowedTypes('filename', ['string']);
        $optionsResolver->addAllowedTypes('file', ['string', 'resource', '\Psr\Http\Message\StreamInterface']);
        $optionsResolver->addAllowedTypes('content', ['string']);

        return $optionsResolver;
    }

    protected function getHeadersOptionsResolver(): \Symfony\Component\OptionsResolver\OptionsResolver
    {
        $optionsResolver = parent::getHeadersOptionsResolver();
        // todo

        return $optionsResolver;
    }

    protected function transformResponseBody(\Psr\Http\Message\ResponseInterface $response, \Symfony\Component\Serializer\SerializerInterface $serializer, ?string $contentType = null)
    {
        $status = $response->getStatusCode();
        $body = (string) $response->getBody();
        if (200 === $status) {
            return $serializer->deserialize($body, 'JoliCode\Slack\CustomRoutes\Model\FilesUploadToURLExternalPostResponse200', 'json');
        }

        return $serializer->deserialize($body, 'JoliCode\Slack\CustomRoutes\Model\FilesUploadToURLExternalPostResponseDefault', 'json');
    }
}

And the test method I have been working with


public function testCompleteUploadExternal(): void
    {
        /* Step 1: Get the upload url */
        $client = $this->createClient();

        $filename = 'test-image.png';

        /** @var FilesGetUploadURLExternalGetResponse200 $response */
        $response = $client->filesGetUploadURLExternal([
            'filename' => $filename,
            'length' => filesize(__DIR__ . '/resources/test-image.png'),
        ]);

        $this->assertTrue($response->getOk());

        $this->assertNotEmpty($response->getUploadUrl());
        $this->assertNotEmpty($response->getFileId());

        // Save values for confirming upload
        $fileId = $response->getFileId();
        $uploadUrl = $response->getUploadUrl();

        /* Step 2: Send file to slack with post method */

        /** @var FilesUploadToURLExternalPostResponse200 $response */
        $response = $client->FileUploadToURLExternal($uploadUrl, [
            'filename' => $filename,
            'content' => file_get_contents(__DIR__ . '/resources/test-image.png'), // , 'r'),
        ]);

//        /** @var FilesUploadToURLExternalPostResponse200 $response */
//        $response = $client->FileUploadToURLExternal($uploadUrl, [
//            'filename' => $filename,
//            'content' => fopen(__DIR__ . '/resources/test-image.png', 'r'),
//        ]);

        $this->assertTrue($response->getOk());

        /* Step 3: confirm upload */
        $filesArr = [
            [
                'id' => $fileId,
                'title' => $filename,
            ],
        ];

        $response = $client->filesCompleteUploadExternal([
            'channel_id' => $_SERVER['SLACK_TEST_CHANNEL'],
            'files' => json_encode($filesArr),
        ]);

        $this->assertTrue($response->getOk());

        $expected = [
            'id' => $fileId,
            'title' => $filename,
        ];

        $this->assertSame($expected, $response['files']);
    }

@damienalexandre
Copy link
Member

Ok thanks for sharing your progress!
Best thing to do is to push everything you have and maybe one of us will be able to finish it (I may - just not sure when).
And have a great vacation 🤗🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants