Skip to content

Conversation

@SylvainSenechal
Copy link

@SylvainSenechal SylvainSenechal commented Oct 2, 2025

Related : scality/backbeat#2679
Replacing the "Backbeat client" built with the AWS SDK V2 in Backbeat, with this new client built with Smithy, related to our migration to the AWS SDK V3

ISSUE: CLDSRVCLT-1

@SylvainSenechal SylvainSenechal force-pushed the improvement/CLDSRVCLT-1 branch from 920e18a to 356c165 Compare October 2, 2025 14:46
Copy link
Author

Choose a reason for hiding this comment

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

I think its nice to have this here as a reference, let me know if you think we should remove it

Copy link
Contributor

@francoisferrand francoisferrand Oct 2, 2025

Choose a reason for hiding this comment

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

it is still in (backbeat) git history, should be enough I think.
would be nice however to link to backbeat file on the PR message and/or commit message

Choose a reason for hiding this comment

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

👍 agree to delete it

@SylvainSenechal SylvainSenechal force-pushed the improvement/CLDSRVCLT-1 branch from 8101fc0 to 103d2ed Compare October 2, 2025 15:22
Copy link
Contributor

@francoisferrand francoisferrand Oct 2, 2025

Choose a reason for hiding this comment

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

should this "build" folder be committed?

looking at package.json, it seems we rebuild them anyway...

Copy link
Author

Choose a reason for hiding this comment

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

We need to have smithy installed to generate the build folder, I feel like it would be more complex if it's not comited ? At what moment would the build generation happen ?
I'm not too sure about this

Choose a reason for hiding this comment

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

Should be in the CI to be honest and the pushed with the release

@restJson1
@sigv4(name: "s3")
@service(sdkId: "cloudserver")
service cloudserver {
Copy link
Contributor

Choose a reason for hiding this comment

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

looking at the bigger picture (i.e. all the APIs supported by cloudserver), this is only one part : i.e. the "Backbeat routes".

eventually (not in this PR ;) we should have other APIs I guess, i.e.

→ given this context, is cloudserver the right name for this specific service?

Copy link
Contributor

Choose a reason for hiding this comment

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

ping

Choose a reason for hiding this comment

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

IMO cloudserver is fine. It's a first iteration so we don't have all of them and I think it's ok. Maybe we should add a TODO.md with which API is missing, the cleanup of the tests (assert of process.env) and the CI? And link them to JIRA ticket

Copy link
Contributor

@francoisferrand francoisferrand Oct 2, 2025

Choose a reason for hiding this comment

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

it is still in (backbeat) git history, should be enough I think.
would be nice however to link to backbeat file on the PR message and/or commit message

},
"dependencies": {
"@aws-sdk/client-s3": "^3.896.0",
"@scality/cloudserverclient": "file:../build/smithy/source/typescript-codegen"
Copy link
Contributor

Choose a reason for hiding this comment

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

  • this works in the same repo (file:..); how will it work from another repo, i.e. with git reference?
  • depending on the "internal" generation path of the package does not look very good..... could we/should we "wrap" this with a top-level file -maybe manually generated- re-exporting tuff from build/smithy/source/typescript-codegen ? So that other packages could directly use the package.json defined at root directory?

Copy link
Contributor

Choose a reason for hiding this comment

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

alternatively, we may push the registry... c.f. scality/Guidelines#206
(may be done in a followup PR, adding github actions workflows to build/... everything)

Copy link
Author

Choose a reason for hiding this comment

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

I tried importing it from backbeat
It worked with this :
"@scality/cloudserverclient": "file:../cloudserverclient"
What I did here in the package.json is if you look at "main", "types", "modules", I provided the path to the build folder :
"main": "build/smithy/source/typescript-codegen/dist-cjs"
So when we release a version of this repo, we should be able to use the library this way :
"@scality/cloudserverclient": "github:scality/cloudserverclient#v1.0.0"

Copy link
Author

Choose a reason for hiding this comment

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

Ended up changing it :
We now have a client wrapper setup at the root project, it was needed to be able to provide this client with the error wrapper needed to handle html/xml error parsing

Copy link
Contributor

@francoisferrand francoisferrand left a comment

Choose a reason for hiding this comment

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

publishing some overall comments, did not yet review the tests or models yet

@SylvainSenechal SylvainSenechal force-pushed the improvement/CLDSRVCLT-1 branch 3 times, most recently from c8442a6 to c6c998f Compare October 2, 2025 20:27
@SylvainSenechal SylvainSenechal force-pushed the improvement/CLDSRVCLT-1 branch 3 times, most recently from 766fbca to 125bc0d Compare October 6, 2025 12:49
@@ -0,0 +1,244 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

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

do we actually need to commit the "build" part?
should it not be dynamically generated?

Copy link
Author

Choose a reason for hiding this comment

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

Discussed. Leave it for now, could be possibly changed later, but needs to setup some more stuff because it would us to be able to install the smithy tool in the pipeline

const raftIdData = await client.send(getRaftIdCommand);
console.log('GetRaftId succeeded:', raftIdData);
console.log('Raft ID:', raftIdData.RaftId);
} catch (err) {
Copy link
Contributor

Choose a reason for hiding this comment

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

we should not catch the error, as it prevents the test framework from noticing : so the test will always succeed...

Copy link
Contributor

Choose a reason for hiding this comment

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

(if this is expected, best to just skip the test for now...)

Copy link
Author

Choose a reason for hiding this comment

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

Ideally these tests should be run in a pipeline as functional tests, for now I ran them manually and I think they are fine this way. I have another ticket for setting up a pipeline, although I think this will not be straightforward to test : We need cloudserver to be started with different setup depending on the api we want to test

@SylvainSenechal SylvainSenechal force-pushed the improvement/CLDSRVCLT-1 branch 5 times, most recently from 0e5f323 to dfc7d0f Compare October 23, 2025 07:48
src/index.ts Outdated
});
}

// S3C has an nginx proxy that can return HTML error responses
Copy link
Author

Choose a reason for hiding this comment

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

"JSONStream": "^1.3.5"
},
"author": "Scality",
"license": "SEE LICENSE IN FILE LICENSE",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be:

Suggested change
"license": "SEE LICENSE IN FILE LICENSE",
"license": "Apache-2.0",


# Nuxt.js build / generate output
.nuxt
dist
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
dist
# For now, the `dist` folder is commited: so we should not ignore it
#dist

@@ -0,0 +1,105 @@
import {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: the test prefix in these files is very redundant ; and the suffix is usually .spec.ts (at least in our code) -> files shoudl be renamed (apis.spec.ts, etc)

Choose a reason for hiding this comment

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

As we are in a test folder, we can just use test.apis.ts or test.apisjs.js. spec.ts is used when test file are located next to the src ?

const data = await client.send(command);
console.log('PutData succeeded:', data.Location);
} catch (err: any) {
console.log('PutData failed:', err);
Copy link
Contributor

Choose a reason for hiding this comment

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

since you catch the error, the test will actually never fail...
--> the whole try/catch is actually not needed, the test framework will autoamtically catch (and probably print) the error...


const command = new PutDataCommand(putInput as any);
const data = await client.send(command);
console.log('PutData succeeded:', data.Location);
Copy link
Contributor

@francoisferrand francoisferrand Oct 29, 2025

Choose a reason for hiding this comment

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

  • do we need the log now that we have a test framework?
  • maybe we want to assert on the value of data.Location ?

}

private createCustomErrorMiddleware = () => {
return (next: any) => async (args: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

it would be worth extracting this middleware from CloudserverClient for clarity, something like XmlErrorMiddleware

@service(sdkId: "cloudserver")
service cloudserver {
version: "2017-07-01",
operations: [
Copy link
Contributor

Choose a reason for hiding this comment

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

is the order here relevant/needed for smithy?
if possible, it may be best to keep the list sorted (alphabetically), to make it easier to see that every route is there and avoid "reordering" later?

$version: "2.0"
namespace cloudserver.client

@idempotent
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure this API is really idempotent : esp. if the versoinId is not specified (but the bucket is versionned), this would create a new version on every call?

@streaming
blob StreamingBlob

@idempotent
Copy link
Contributor

Choose a reason for hiding this comment

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

should this be considered idempotent?

every call with write new data (in the backend), and update the specified Object version to 'point' to that data:

  • from a user's perspective, should be idempotent indeed (as long as the content of payload is also the same)
  • from the server's perspective, data would however get written repeatedly, and the (internal) "key" of the data would get updated. (hopefully, the previous data would not be lost -creating orphan-, but not sure...)

do you know how this should be considered & what is the (practical) impact of this trait?

@httpHeader("X-Scal-Request-Uids")
RequestUids: String,
@httpPayload
@default("")
Copy link
Contributor

Choose a reason for hiding this comment

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

is there really a need for such default? (or is it a side-effect of smithy implementation, where an "empty" body is just skipped and we don't get anything without default?)

@streaming
blob StreamingBlob

@idempotent
Copy link
Contributor

Choose a reason for hiding this comment

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

may not be idempotent, esp. in case bucket is versionned and X-Scal-Version-Id is not set

Comment on lines +57 to +77

list LocationMDList {
member: LocationMDObj
}

structure LocationMDObj {
/// Storage key for this location
key: String,
/// Size of the data stored at this location
size: Integer,
/// Start position/offset for this data segment
start: Integer,
/// Name of the data store where this is located
dataStoreName: String,
/// Type of the data store (e.g., file, mem, etc.)
dataStoreType: String,
/// ETag from the data store for this location
dataStoreETag: String,
/// Version ID in the data store for this location
dataStoreVersionId: String
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: these are shared types, can they be moved to a separate smithy file? (locationMD.smithy)

blob StreamingBlob

/// Uploads a part for a multipart upload to multiple backend storage
@idempotent
Copy link
Contributor

Choose a reason for hiding this comment

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

does this not create a new part on every call, making it non-idempotent?

@required
@httpLabel
Bucket: String,
@required
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: skip line between fields (like in other files), makes it much easier to read


@httpHeader("X-Scal-Tags")
Tags: String,
@httpHeader("X-Scal-Request-Uids")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@httpHeader("X-Scal-Request-Uids")
@httpHeader("X-Scal-Request-Uids")


@httpHeader("X-Scal-Upload-Id")
UploadId: String,
@httpHeader("X-Scal-Request-Uids")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@httpHeader("X-Scal-Request-Uids")
@httpHeader("X-Scal-Request-Uids")

@httpLabel
@required
Bucket: String,

Copy link
Contributor

@francoisferrand francoisferrand Oct 29, 2025

Choose a reason for hiding this comment

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

extra space at EOL (here and other places)

/// Limits the response to keys that begin with the specified prefix
@httpQuery("prefix")
Prefix: String,
@httpHeader("X-Scal-Request-Uids")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@httpHeader("X-Scal-Request-Uids")
@httpHeader("X-Scal-Request-Uids")

/// Limits the response to keys that begin with the specified prefix
@httpQuery("prefix")
Prefix: String,
@httpHeader("X-Scal-Request-Uids")
Copy link
Contributor

@francoisferrand francoisferrand Oct 29, 2025

Choose a reason for hiding this comment

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

Suggested change
@httpHeader("X-Scal-Request-Uids")
@httpHeader("X-Scal-Request-Uids")

(in most files)

@required
@httpLabel
Bucket: String,
@httpHeader("X-Scal-Request-Uids")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@httpHeader("X-Scal-Request-Uids")
@httpHeader("X-Scal-Request-Uids")

output: GetObjectOutput,
}

structure GetObjectInput {
Copy link
Contributor

Choose a reason for hiding this comment

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

is this not the same type used in AWS ?
could we import those types and inherit from them (for custom/extra fields we support)?

something like:

structure GetObjectInput extends aws.GetObjectInput {
    @httpHeader("X-Scal-Request-Uids")
    RequestUids: String
}

$version: "2.0"
namespace cloudserver.client

@http(method: "POST", uri: "/_/backbeat/index/{Bucket}?operation=delete")
Copy link
Contributor

@francoisferrand francoisferrand Oct 29, 2025

Choose a reason for hiding this comment

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

don't we need this:

@idempotent
@suppress(["HttpMethodSemantics.UnexpectedPayload"])

(it has a body in the definition, and delete should be idempotent...)
(or maybe there should not be a body?)

"target": "es2020",
"module": "commonjs",
"lib": ["es2020"],
"outDir": "./dist",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: should we output to ./buid instead, so all the generated code is in a single place?

@francoisferrand
Copy link
Contributor

LGTM, some cleanup (extra space / missing line in models...) to do.

Not sure what is the effect of @idempotent, but in some case this seems unclear if appropriate: so we may want to dig a bit more

@@ -0,0 +1,105 @@
import {

Choose a reason for hiding this comment

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

As we are in a test folder, we can just use test.apis.ts or test.apisjs.js. spec.ts is used when test file are located next to the src ?

@restJson1
@sigv4(name: "s3")
@service(sdkId: "cloudserver")
service cloudserver {

Choose a reason for hiding this comment

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

IMO cloudserver is fine. It's a first iteration so we don't have all of them and I think it's ok. Maybe we should add a TODO.md with which API is missing, the cleanup of the tests (assert of process.env) and the CI? And link them to JIRA ticket

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.

5 participants