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

Implement converter #2

Merged
merged 300 commits into from
May 14, 2019
Merged

Implement converter #2

merged 300 commits into from
May 14, 2019

Conversation

bookmoons
Copy link
Contributor

Implements the har-to-k6 converter, including API and CLI interfaces.

CLI usage:

har-to-k6 archive.har -o loadtest.js

API usage:

const { main, compat } = await liHARToK6Script(archive);

Builds an optimized compatibility layer when needed.
Validates with what I hope are useful error messages. validate() exposed for separate use.

There are some examples that can be converted in example. example/full.har tries to exercise everything. It should run a successful live test against httpbin.

@bookmoons
Copy link
Contributor Author

Didn't realize it would be used in the browser. Is that a requirement? Think I'd have to revisit the compatibility layer to make that work, because it uses temp directories in the file system.

It looks like it is possible to bundle it with browserify. But the compat layer prevents it running.

@bookmoons
Copy link
Contributor Author

I see that is indeed in the converter spec. Should have read that more carefully. Looking into this.

@bookmoons
Copy link
Contributor Author

bookmoons commented May 8, 2019

Well, tried to get browserify to run in the browser but I'm not having any luck. It does strange things with the file system.

I've handled this by prebundling all possible compat layers. It adds a few megabytes.

The code formatter, awkwardly, tries to load config from the file system. So I've had to disable it in the browser. Tried to generate in that format all the way through so it was redundant anyway. It should come out OK.

If you do npm run bundle you'll get a bundle in build/har-to-k6.js. It takes a minute to build and optimize everything. I was able to convert full.har in browser. Also added a section describing this to the readme.

@legander
Copy link
Collaborator

legander commented May 8, 2019

I'm running in to this error when loading the bundle in the browser
stacktrace

@bookmoons
Copy link
Contributor Author

Shoot, I see it too in Chrome. Looking into that.

Default behavior of the terser optimizer transforms ASCII escapes
in regular expressions to raw Unicode characters. Chrome
disapproves of the output.

Disables Unicode characters in regular expressions and strings.
@bookmoons
Copy link
Contributor Author

Pushed a fix to this. One of the optimizers was tweaking regular expressions in a way Chrome didn't like. Disabled that behavior.

@legander
Copy link
Collaborator

legander commented May 9, 2019

Yes it does work, sweet! 🎉
Strangely though it only works when loading the bundle with a script tag. When trying to import the convert method the ES way I still see that same error :/
Would be great if it was possible to import it like a ES module, can you please take a look if it is a small fix?

import { liHARToK6Script } from "../../har-to-k6/build/har-to-k6";

@bookmoons
Copy link
Contributor Author

Strange, will look into it.

@bookmoons
Copy link
Contributor Author

I'm not sure there's a way to do this. Tried bundling some modules with different settings, but none of them get an importable module. Browserify seems to be targeting usage through the script tag.

@legander
Copy link
Collaborator

I managed to get rid of that error by adding <meta charset="utf-8"> to head in my index file... 😅
The ES module importing works fine now! 🎉
I've found one last thing. The example example/variable.har generates an invalid http.get call with null as a second arg where it should instead be the config object.

import http from "k6/http";
import { jsonpath } from "./compat.js";

export default function() {
  let response;

  const vars = {};

  response = http.post(
    "http://api.example.com/authenticate",
    "{\"username\":\"admin\",\"password\":\"123\"}",
    {
      headers: {
        "Content-Type": "application/json",
      },
    }
  );
  vars["accessToken"] = jsonpath.query(response.json(), "user.token")[0];

  response = http.get("http://api.example.com/users", null, {
    headers: {
      Authorization: `Bearer ${vars["accessToken"]}`,
    },
  });
}

@bookmoons
Copy link
Contributor Author

Updated that.

@legander
Copy link
Collaborator

Great, thank you!
I was thinking about this compat file. Would it require a lot to remove/replace formUrlEncode and jsonpath from the dependencies?
If instead jsonpath was a part of K6 js lib and importable like import { jsonpath } from "k6/encoding”.
formUrlEncode could be replaced with new URLSearchParams({ key: “value” }).toString(). I even think that K6 automatically formurlencodes JSON data if Content-Type “application/x-www-form-urlencoded“ is specified. https://docs.k6.io/docs/post-url-body-params

One of the use cases for this converter is to be used in a browser via a UI form drag and drop type of thing. So the idea is that a user should be able to compose a K6 script by click/drag and input. I think It would create a better UX if we could reduce the need for the compat file to a minimal.
Tell me what you think, is it possible?

@bookmoons
Copy link
Contributor Author

It would take some doing.

If instead jsonpath was a part of K6 js lib and importable like import { jsonpath } from "k6/encoding”.
formUrlEncode could be replaced with new URLSearchParams({ key: “value” }).toString().

I could take these up as improvements under new issues. I see there are roadmap issues to add them to the engine grafana/k6#992 grafana/k6#991. Maybe I could evaluate the cost to do those.

The converter tries hard to use builtin features wherever possible. Most of the time that formUrlEncode piece shouldn't be activated:

  • It only happens in the narrow situation where 1) there's a multivalue parameter (eg search=kitten&search=puppy); and 2) there's a parameter with a variable (eg session=${session}). The variable resolution needs to happen at runtime but the k6 object interface only accepts single value parameters. So it needs to be constructed at runtime outside k6.
  • Where there's a multivalue parameter but no variables the converter handles this at convert time and outputs the resulting string. That prevents loading the library when it can be done externally.
  • Where all parameters are single value, with variables or without, it can be done by k6 so it's done through the object interface. I think this should be the most common case.

MimeBuilder usage does a similar thing of minimizing circumstances where it's loaded.

@legander
Copy link
Collaborator

Aight good to know, I'll add an issue that can be revisited once K6 supports those apis.
I think we can close this pr now. Thank you and good work on this project! 💯🎉 You can claim your bounty now!

@legander legander merged commit 5662146 into grafana:master May 14, 2019
@bookmoons
Copy link
Contributor Author

Thank you for the review.

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.

2 participants