Skip to content

Commit

Permalink
Added tabs in query editor section.
Browse files Browse the repository at this point in the history
Added post request scripts.
  • Loading branch information
imolorhe committed Mar 6, 2021
1 parent 9800a3e commit 27560dc
Show file tree
Hide file tree
Showing 35 changed files with 1,524 additions and 971 deletions.
9 changes: 3 additions & 6 deletions .github/PULL_REQUEST_TEMPLATE.md
@@ -1,13 +1,10 @@
### Fixes #

...
<!-- Mention the issues this PR addresses -->

### Checks

- [ ] Ran `yarn test-build`
- [ ] Updated relevant documentations

### Changes proposed in this pull request:

-
-
-
<!-- Describe the changes being introduced in this PR -->
39 changes: 30 additions & 9 deletions docs/docs/features/prerequest-scripts.md
Expand Up @@ -2,17 +2,19 @@
parent: Features
---

## Pre request scripts
## Pre and post request scripts

Ever wanted to use dynamic data in your request headers, URLs, before sending the request? Pre request scripts enables you do that. With pre request scripts, you can set environment variables from your cookies or create any combination of values for your environment variables. This comes in handy for environments where things like CSRF tokens are used to verify the requests sent to the server.

### Available API for Pre request scripts
You might want to perform some extra logic with the response from a request. For example, setting an environment variable from a response header, or from the result of a query. Post request scripts enables you do just that.

Pre request scripts support all JavaScript syntax supported in the latest [ecmascript 2019 (ES10) specification](https://tc39.es/ecma262/) (except **with** and **label** statements, but those are discouraged anyway). These include things like `[].flat()`, `[].flatMap()`, `Object.fromEntries()`, etc.
### Available API for request scripts

There is also a global object `altair` available within the context of the pre request script containing helper methods for interacting with altair.
Request scripts support all JavaScript syntax supported in the latest [ecmascript 2019 (ES10) specification](https://tc39.es/ecma262/) (except **with** and **label** statements, but those are discouraged anyway). These include things like `[].flat()`, `[].flatMap()`, `Object.fromEntries()`, etc.

#### altair.data
There is also a global object `altair` available within the context of the request script containing helper methods for interacting with altair.

### altair.data

This contains data used to process your GraphQL request.

Expand All @@ -24,7 +26,7 @@ This contains data used to process your GraphQL request.

**altair.data.environment** - The formatted environment object containing the serialized set of environment data before your request is sent.

#### altair.helpers
### altair.helpers

This contains a number of helper methods for carrying out basic tasks, like interacting with altair, making network requests, etc.

Expand All @@ -34,7 +36,7 @@ This contains a number of helper methods for carrying out basic tasks, like inte
altair.helpers.getEnvironment('api_key')
```

**altair.helpers.setEnvironment(key: string, val: any)** - Sets the environment variable for the specified key, overriding the environment variable for the current request.
**altair.helpers.setEnvironment(key: string, val: any, activeEnvironment?: boolean)** - Sets the environment variable for the specified key, overriding the environment variable for the current request. You can also pass an extra boolean parameter to indicate if the environment variable should also be set in the currently active environment.

```js
altair.helpers.setEnvironment('api_key', 'a482djksd289xxxxxxxxx');
Expand Down Expand Up @@ -77,19 +79,38 @@ if (nowInSeconds() >= Number(tokenExpiry)) {
// res => { "token": "abcd", "expiry": 3600 }

// Store the received token and expiry in localStorage
// Alternatively you can set this in the active environment
// altair.helpers.setEnvironment("token", res.token);
// altair.helpers.setEnvironment("token_expiry", nowInSeconds() + res.expiry);
localStorage.setItem("token", res.token);
localStorage.setItem("token_expiry", nowInSeconds() + res.expiry);
}

// Retrieev the token from localStorage
// Retrieve the token from localStorage
// const token = altair.helpers.getEnvironment("token");
const token = localStorage.getItem("token");

// Set the token as the `token_env` environment variable in Altair
altair.helpers.setEnvironment('token_env', token);
// You can use the environment variables in Altair after setting it by following this blog post: https://sirmuel.design/altair-becomes-environment-friendly-%EF%B8%8F-f9b4e9ef887c
```

#### altair.importModule

### altair.response (Available in post request script)

This contains response from your GraphQL request. **Note: This is only available in post request scripts.**

**altair.response.headers** - The response headers sent from the server. **Note: Due to some limitations on response headers in the browser, it is advisable to use the desktop apps if you need to use the response headers**

**altair.response.statusCode** - The status code of the response.

**altair.response.requestType** - Indicates the type of request being sent. Values are `query`, `introspection` or `subscription`.

**altair.response.responseTime** - The total time of the request

**altair.response.body** - The response body

### altair.importModule

This allows you to import some modules that are made available in the pre request script editor. It *returns a promise* that resolves with the imported module.

Expand Down
2 changes: 2 additions & 0 deletions packages/altair-app/src/app/components/components.module.ts
Expand Up @@ -32,6 +32,7 @@ import { EditCollectionDialogComponent } from './edit-collection-dialog/edit-col
import { EnvironmentManagerComponent } from './environment-manager/environment-manager.component';
import { FancyInputComponent } from './fancy-input/fancy-input.component';
import { PreRequestEditorComponent } from './pre-request-editor/pre-request-editor.component';
import { PostRequestEditorComponent } from './post-request-editor/post-request-editor.component';
import { SchemaFormModule } from './schema-form/schema-form.module';
import { PluginManagerComponent } from './plugin-manager/plugin-manager.component';
import { ElementWrapperComponent } from './element-wrapper/element-wrapper.component';
Expand Down Expand Up @@ -60,6 +61,7 @@ const COMPONENTS = [
EnvironmentManagerComponent,
FancyInputComponent,
PreRequestEditorComponent,
PostRequestEditorComponent,
PluginManagerComponent,
ElementWrapperComponent,
];
Expand Down
Expand Up @@ -108,6 +108,9 @@ export class EnvironmentManagerComponent implements OnInit, DoCheck, OnChanges {
clearTimeout(refreshEditorTimeout);
}, 300);
}
if (changes.environments && changes.environments.currentValue) {
this.selectEnvironment(this.environments.activeSubEnvironment || 'base');
}
}

onEditorChange(content: string) {
Expand Down
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PreRequestEditorComponent should render correctly 1`] = `
<app-pre-request-editor>
<div
class="pre-request-editor"
>
<div
class="pre-request-editor__toggle-wrapper"
>
<label
nz-checkbox=""
>
</label>
</div>
<ngx-codemirror
class="pre-request-textarea mousetrap"
/>
<small>
<a
href="https://altair.sirmuel.design/docs/features/prerequest-scripts.html"
target="_blank"
>
<app-icon
name="info"
/>
</a>
</small>
</div>
</app-pre-request-editor>
`;
@@ -0,0 +1,23 @@
<div class="post-request-editor">
<div class="post-request-editor__toggle-wrapper spacer--small">
<label
nz-checkbox
[ngModel]="postRequest?.enabled"
(ngModelChange)="postRequestEnabledChange.next($event)"
>{{ 'REQUEST_SCRIPT_ENABLE_TEXT' | translate }}</label>
</div>
<ngx-codemirror
#editor
class="post-request-textarea mousetrap"
[options]="postRequestEditorConfig"
[ngModel]="postRequest?.script"
(ngModelChange)="postRequestScriptChange.emit($event)"
></ngx-codemirror>

<small>
<a href="https://altair.sirmuel.design/docs/features/prerequest-scripts.html" target="_blank">
<app-icon name="info"></app-icon>
{{ 'LEARN_MORE_TEXT' | translate }}
</a>
</small>
</div>
@@ -0,0 +1,35 @@
import { expect, describe, it } from '@jest/globals';

import { PostRequestEditorComponent } from './post-request-editor.component';
import { SharedModule } from 'app/modules/shared/shared.module';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NgxTestWrapper, mount } from '../../../testing';
import { MockModule } from 'ng-mocks';

describe('PostRequestEditorComponent', () => {
let wrapper: NgxTestWrapper<PostRequestEditorComponent>;

beforeEach(async() => {
wrapper = await mount({
component: PostRequestEditorComponent,
imports: [
MockModule(SharedModule),
],
schemas: [ NO_ERRORS_SCHEMA ],
propsData: {
preRequest: {
enabled: true,
script: '',
}
},
});
});

it('should create', () => {
expect(wrapper.componentInstance).toBeTruthy();
});

it('should render correctly', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
@@ -0,0 +1,119 @@
import {
Component,
OnChanges,
EventEmitter,
Input,
Output,
ViewChild,
ElementRef,
DoCheck,
AfterViewInit
} from '@angular/core';

// Import the codemirror packages
import * as Codemirror from 'codemirror';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/hint/javascript-hint';
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/fold/foldcode';
import 'codemirror/addon/fold/foldgutter';
import 'codemirror/addon/fold/brace-fold';
import 'codemirror/addon/fold/indent-fold';
// import 'codemirror/addon/display/autorefresh';
import 'codemirror/keymap/sublime';
import 'codemirror/mode/javascript/javascript';
import { handleEditorRefresh } from 'app/utils/codemirror/refresh-editor';

const AUTOCOMPLETE_CHARS = /^[a-zA-Z0-9_]$/;

@Component({
selector: 'app-post-request-editor',
templateUrl: './post-request-editor.component.html',
styles: []
})
export class PostRequestEditorComponent implements AfterViewInit, OnChanges, DoCheck {

@Input() postRequest: any = {};
@Output() postRequestScriptChange = new EventEmitter();
@Output() postRequestEnabledChange = new EventEmitter();

@ViewChild('editor', { static: true }) editor: ElementRef & { codeMirror: CodeMirror.Editor };

postRequestEditorConfig = {
mode: 'javascript',
lineWrapping: true,
lineNumbers: true,
foldGutter: true,
autoRefresh: true,
dragDrop: false,
autoCloseBrackets: true,
keyMap: 'sublime',
theme: 'default request-script-editor mousetrap',
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
extraKeys: {
'Ctrl-Space': 'autocomplete',
'Cmd-/': (cm: CodeMirror.Editor) => cm.execCommand('toggleComment'),
'Ctrl-/': (cm: CodeMirror.Editor) => cm.execCommand('toggleComment'),
},
hintOptions: {
completeSingle: false,
globalScope: this.createGlobalScope(),
},
};

constructor() {}

ngAfterViewInit() {
if (this.editor?.codeMirror) {
(this.editor.codeMirror as any).on('keyup', (cm: CodeMirror.Editor, event: KeyboardEvent) => this.onKeyUp(cm, event));
}
}

ngOnChanges() {
}

ngDoCheck() {
// Refresh the query result editor view when there are any changes
// to fix any broken UI issues in it
handleEditorRefresh(this.editor && this.editor.codeMirror);
}

/**
* Handles the keyup event on the query editor
* @param cm
* @param event
*/
onKeyUp(cm: CodeMirror.Editor, event: KeyboardEvent) {
if (AUTOCOMPLETE_CHARS.test(event.key)) {
this.editor.codeMirror.execCommand('autocomplete');
}
}

private createGlobalScope() {
const xy = {
altair: {
helpers: {
getEnvironment: null,
setEnvironment: null,
getCookie: null,
request: null
},
importModule: null,
response: {
requestType: null,
responseTime: null,
statusCode: null,
body: null,
headers: null,
},
}
};

return JSON.parse(JSON.stringify(xy), (k, v) => {
if (v && typeof v === 'object' && !Array.isArray(v)) {
return Object.assign(Object.create(null), v);
}
return v;
});
}
}
@@ -1,5 +1,5 @@
<div class="pre-request-editor">
<div class="pre-request-editor__toggle-wrapper">
<div class="pre-request-editor__toggle-wrapper spacer--small">
<label
nz-checkbox
[ngModel]="preRequest.enabled"
Expand Down
Expand Up @@ -23,7 +23,6 @@ import 'codemirror/addon/fold/indent-fold';
import 'codemirror/keymap/sublime';
import 'codemirror/mode/javascript/javascript';
import { handleEditorRefresh } from 'app/utils/codemirror/refresh-editor';
import { PreRequestService } from 'app/services';

const AUTOCOMPLETE_CHARS = /^[a-zA-Z0-9_]$/;

Expand All @@ -49,7 +48,7 @@ export class PreRequestEditorComponent implements AfterViewInit, OnChanges, DoCh
dragDrop: false,
autoCloseBrackets: true,
keyMap: 'sublime',
theme: 'default pre-request-editor mousetrap',
theme: 'default request-script-editor mousetrap',
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
extraKeys: {
'Ctrl-Space': 'autocomplete',
Expand Down

0 comments on commit 27560dc

Please sign in to comment.