question: how do I flatten nested Outputs? backgr...
# typescript
a
question: how do I flatten nested Outputs? background: I need to upload multiple blobs from locally generated resources (because I deploy multiple services). so I came up with this:
Copy code
function uploadBinaries() {
    const storageAccount = new storage.StorageAccount("deployments", {
        resourceGroupName: resourceGroupName,
        kind: storage.Kind.StorageV2,
        sku: {
            name: storage.SkuName.Standard_LRS,
        },
    });
    const storageContainer = new storage.BlobContainer("binaries", {
        resourceGroupName: resourceGroupName,
        accountName: storageAccount.name,
        publicAccess: storage.PublicAccess.None,
    });

    const blobs= filesystem.readdirSync(cfg.require("PackagePath")).map(archive => new storage.Blob(archive, {
        resourceGroupName: resourceGroupName,
        accountName: storageAccount.name,
        containerName: storageContainer.name,
        source: new pulumi.asset.FileAsset(archive)
    }));


    const urls= pulumi.all(blobs).apply(bs => bs.map(b => getUrl(b)));

    function getUrl(blob: storage.Blob) : pulumi.Output<string> {
        return  pulumi.all(
            [storageAccount.name, storageContainer.name, resourceGroupName, blob.name, blob.id]).apply(
                ([accountName, containerName, rgName, blobName]) => getSASToken(accountName, containerName, rgName, blobName));
        
    }
    function getSASToken(storageAccountName: string, storageContainerName: string, resourceGroupName: string, blobName: string): pulumi.Output<string> {
        const blobSAS = storage.listStorageAccountServiceSAS({
            accountName: storageAccountName,
            protocols: storage.HttpProtocol.Https,
            sharedAccessStartTime: format(new Date()),
            sharedAccessExpiryTime: format(nextYear()),
            resource: storage.SignedResource.C,
            resourceGroupName: resourceGroupName,
            permissions: storage.Permissions.R,
            canonicalizedResource: "/blob/" + storageAccountName + "/" + storageContainerName,
            contentType: "application/json",
            cacheControl: "max-age=5",
            contentDisposition: "inline",
            contentEncoding: "deflate",
        });
        return pulumi.interpolate `https://${storageAccountName}.<http://blob.core.windows.net/${storageContainerName}/${blobName}?${blobSAS.then(x|blob.core.windows.net/${storageContainerName}/${blobName}?${blobSAS.then(x> => x.serviceSasToken)}`;

        function format(when: Date){
            const year= when.getFullYear();
            const month= 1+when.getMonth();
            const day= 1+when.getDay();
            return `${year}-${pad(month)}-${pad(day)}`;
        
            function pad(n: number){
                return n.toString().padStart(2, '0');
            }
        }
        function nextYear():Date {
            const result= new Date();
            result.setFullYear(result.getFullYear()+1);
            return result;
        }
    }
}
the problem is that
urls
is now of type
Output<Output<string>[]>
instead of
Output<string[]>
. How do I get there? related to that, I will actually need a mapping of archive=>sas-url. This I would create once I got the flattened output, but I thought to mention it in case it has any bearing.
l
Is it not feasible to move the getUrl logic into the previous
map
, which is currently returning storage.Blobs? And instead return URLs there?
(Also: large chunks of code like this benefit from the Slack Text Snippet format)
But in general: you may want
reduce
instead of
map
, so that you're creating a single
Output<string[]>
that you're amending, instead of an
Output<Output<string[]>>
that is returned from
pulumi.all()
.
a
thanks for the pointers! (and about text snippet - I didn't know about that, sorry)