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

Feature/several #312

Merged
merged 41 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
71ff464
Added MimeService
Jun 20, 2023
1b83d74
Added tests for MimeService
Jun 21, 2023
5930e01
Updated unit test
Jun 21, 2023
7e2bc6d
Updated sample
Jun 21, 2023
4b240ac
Updated changelog
Jun 21, 2023
02c6f30
Updated integration tests
Jun 21, 2023
a492d29
Show executed stub ID when hovering over a request
Jun 21, 2023
00de385
Updated changelog
Jun 21, 2023
771b22f
Docs fix
Jun 21, 2023
c76dc1f
Fixed redirects
Jun 21, 2023
d1e8813
Fixed unit and integration tests
Jun 21, 2023
0f69157
Updated docs
Jun 25, 2023
779bccc
Updated CHANGELOG
Jun 25, 2023
2dccb71
UI copy fix
Jun 25, 2023
cf2824d
Cut off the number of requests in UI based on the setting "oldRequest…
Jun 25, 2023
f6b96d4
Added IDateTime method to get UTC now as UNIX timestamp
Jun 25, 2023
8b4f7b3
Prepend UNIX timestamp to request filenames
Jun 25, 2023
f9f36c1
Added paging and built paging up to FileSystemStubSource
Jun 25, 2023
71fc14c
Added paging to InMemoryStubSource.cs
Jun 25, 2023
ea54d8c
Added paging for RelationalDbStubSource MySQL
Jun 25, 2023
cc518e7
Small fix in stub sources
Jun 26, 2023
a01f9d8
Implemented the correct queries for the Sqlite and MSSQL query stores
Jun 26, 2023
21a7e4e
Fixed unit tests for now
Jun 26, 2023
4af74b8
Added unit tests for handlers
Jun 26, 2023
e2f15dc
Added extra unit tests for FileSystemStubSource
Jun 26, 2023
46a1628
Added tests for InMemoryStubSource
Jun 26, 2023
dc5c959
Added unit tests for RelationalDbStubSource
Jun 26, 2023
301e9fd
Added integration tests
Jun 27, 2023
f6ae50b
Added Postman integration tests
Jun 29, 2023
6dcb7d2
Added paging to requests in REST call
Jun 29, 2023
b6ef98a
Added "load more requests" button to Requests vue
Jun 30, 2023
4fb034c
Added setting for requests paging
Jun 30, 2023
f4c813e
Use new setting in requests page
Jun 30, 2023
eed6fc0
Added "Load all requests" button
Jun 30, 2023
3dd98ab
Updates in docs and changelog
Jun 30, 2023
593d08e
Updated client with new headers
Jun 30, 2023
bac9a55
Updated unit tests
Jun 30, 2023
a9c6dfa
Updated CHANGELOG again
Jun 30, 2023
faef40d
Small UI fixes
Jun 30, 2023
5e23545
Small UI fix
Jun 30, 2023
2a45c40
Doc fix in build process
Jun 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Build.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ RUN cd docs/httpl-docs && pip install mkdocs && python sync.py && mkdocs build &
# Build UI
FROM node:18 AS gui-build-env
WORKDIR /app
COPY --from=doc-build-env /app/site/. ./docs

COPY . ./
RUN cd gui && npm install && npm run build
RUN cd gui && rm -rf public/docs && mv ../docs public && npm install && npm run build

# Run .NET unit tests
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS test-env
Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[vnext]
[2023.6.30]
- Split form helper buttons into 4 separate buttons (https://github.com/dukeofharen/httplaceholder/pull/305).
- Updated the CodeMirror editor from 5 to 6 (https://github.com/dukeofharen/httplaceholder/pull/305).
- Added filter for variable handlers in the UI (https://github.com/dukeofharen/httplaceholder/pull/305).
Expand All @@ -13,6 +13,15 @@
- Added string / regex replacement response writer (https://github.com/dukeofharen/httplaceholder/pull/309).
- Added SignalR connection for stubs (https://github.com/dukeofharen/httplaceholder/pull/310).
- When generating a stub based on a request, also take the saved saved response (if there is one) into account (https://github.com/dukeofharen/httplaceholder/pull/311).
- When using the file response writer and no specific content type is set; determine the content type based on the file extension (https://github.com/dukeofharen/httplaceholder/pull/312).
- Show executing stub ID when hovering over a request in the UI (https://github.com/dukeofharen/httplaceholder/pull/312).
- Updated redirect response writer. `permanentRedirect` now returns HTTP 308 (HTTP 301) before and `movedPermanently` returns HTTP 301 (https://github.com/dukeofharen/httplaceholder/pull/312).
- Small fix in UI: when copying the request / response body, copy either the raw or "rendered" body based on your selection. At first, only the raw contents were copied (https://github.com/dukeofharen/httplaceholder/pull/312).
- Added paging to requests endpoints and the UI for more responsive UI (https://github.com/dukeofharen/httplaceholder/pull/312).

# BREAKING CHANGES
- `permanentRedirect` now returns HTTP 308 instead of HTTP 301. Use `movedPermanently` to return HTTP 301 instead.
- The way requests are stored when using the file store is slightly changed. This means that when you update to this version, the requests in the API and UI are not returned in the right order. New requests are returned in the right order though.

[2023.4.25]
- Upgraded to .NET 7 (https://github.com/dukeofharen/httplaceholder/pull/293).
Expand Down
20 changes: 17 additions & 3 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1403,7 +1403,7 @@ If you don't specify the full path, HttPlaceholder will look in the same folder

#### Scenario 2

You can also use the full path to a file.
You can also use the full path to a file. NOTE! This only works if you enable the setting [allow global file search](#allow-global-file-search-optional). This setting is added as a security measure.

```yml
- id: image-file
Expand Down Expand Up @@ -1496,7 +1496,7 @@ In this case, a value between 10.000 and 20.000 milliseconds will be randomly pi

## Permanent and temporary redirects

The permanent and temporary redirect response writers are short hands for defining redirects in you stub. If you set an URL on the "temporaryRedirect" property, HttPlaceholder will redirect the user with an HTTP 307, and when you use the "permanentRedirect" an HTTP 301.
The permanent and temporary redirect response writers are short hands for defining redirects in you stub. If you set an URL on the `temporaryRedirect` property, HttPlaceholder will redirect the user with an HTTP 307, when you use the `permanentRedirect` an HTTP 308 and when you use `movedPermanently` an HTTP 301 is returned..

```yml
- id: temp-redirect
Expand All @@ -1520,6 +1520,17 @@ The permanent and temporary redirect response writers are short hands for defini
permanentRedirect: https://reddit.com
```

```yml
- id: moved-permanently
conditions:
method: GET
url:
path:
equals: /moved-permanently
response:
movedPermanently: https://ducode.org
```

## Update line endings

In some cases, you might want to enforce which types of line endings are returned. Some software might only react correctly on Windows or Unix line endings. Besides that, GIT might change the line endings when you commit your stub file, so it is not always clear what the actual line endings are. For setting the line endings specifically, the `lineEndings` response writer was introduced. The supported values are `unix` and `windows`. Here are 2 examples:
Expand Down Expand Up @@ -2666,16 +2677,18 @@ When you run the stub, you can just go to `http://placeholder/ph-ui`. If you've

## Requests page

On the requests page you can see all requests made to HttPlaceholder. It will show which URL was called and if a valid stub was found for the requests. You can also open a specific request and view the details of this request.
On the requests page you can see all requests made to HttPlaceholder. It will show which URL was called and if a valid stub was found for the requests. You can also open a specific request and view the details of this request. By default, paging is enabled for the requests. You can change the paging (or disable it) on the [settings page](#settings-page).

![](img/ui/requests_overview.png)

You have several options here.

- Refresh: fetches the requests from HttPlaceholder.
- Load all requests: loads all request currently available in HttPlaceholder, disregarding the number of requests per page.
- Delete all requests: as the name says, deletes all the requests from HttPlaceholder.
- Filter on stub ID, request ID or URL: a filter search box where you can filter requests either on the executed stub ID, the request correlation ID or the URL.
- Tenant / category name: a filter for filtering requests that are part of a specific tenant (see [tenants](#tenants)).
- Load more requests: loads a new batch of n requests (as defined on the [settings page](#settings-page)).

### Request details

Expand Down Expand Up @@ -2870,6 +2883,7 @@ On the settings page you can configure all kinds of settings for HttPlaceholder
- Dark theme: turns the dark theme on or off.
- Persist search filters on stubs and request screens: when this option is on, the filters on the stubs and is persisted between page navigations.
- Store response for request: whenever this settings is enabled, all the responses that are sent to the client are saved alongside the requests. Clicking this checkbox will send a request to the HttPlaceholder configuration API.
- Default number of requests on the request page. This number determines the number of requests that are loaded by default on the requests page. A "Load more requests" button is shown whenever there are more requests to load. If you set this value to `0`, the paging is disabled and all requests are always loaded.
- View the configuration HttPlaceholder was started with.

# Tools and client libraries
Expand Down
Binary file modified docs/img/ui/requests_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/ui/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions docs/samples/05-base64-file.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
response:
statusCode: 200
file: cat_file.jpg
headers:
Content-Type: image/jpeg

# A simple GET request script that contains a base64 encoded string (this can for example be a binary). In this case, just go to http://localhost:{port}/cat.jpg
- id: image-base64
Expand Down
19 changes: 12 additions & 7 deletions gui/src/components/request/Request.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<template>
<accordion-item @buttonClicked="showDetails" :opened="accordionOpened">
<template v-slot:button-text>
<span class="request-header">
<span
class="request-header"
:title="executed ? 'Executed stub: ' + executingStubId : ''"
>
<Method
v-if="overviewRequest.method"
:method="overviewRequest.method"
/>
<span class="ms-sm-1 request-url" :title="overviewRequest.url">{{
overviewRequest.url
}}</span>
<span class="ms-sm-1 request-url">{{ overviewRequest.url }}</span>
<span class="ms-sm-1">
<span>(</span>
<span
Expand Down Expand Up @@ -81,11 +82,14 @@ export default defineComponent({
// Functions
const getRequestTime = () => props.overviewRequest.requestEndTime;
const correlationId = () => props.overviewRequest.correlationId;
const executed = () => props.overviewRequest.executingStubId;
const getTimeFromNow = () => formatFromNow(getRequestTime());

// Computed
const requestDateTime = computed(() => formatDateTime(getRequestTime()));
const executed = computed(() => !!props.overviewRequest.executingStubId);
const executingStubId = computed(
() => props.overviewRequest.executingStubId
);

// Data
const timeFromNow = ref(getTimeFromNow());
Expand Down Expand Up @@ -134,14 +138,14 @@ export default defineComponent({
try {
await requestStore.deleteRequest(correlationId());
success(resources.requestDeletedSuccessfully);
emit("deleted");
emit("deleted", correlationId());
} catch (e) {
handleHttpError(e);
}
};

return {
executed: executed(),
executed,
requestDateTime,
timeFromNow,
refreshTimeFromNowInterval,
Expand All @@ -150,6 +154,7 @@ export default defineComponent({
accordionOpened,
createStub,
deleteRequest,
executingStubId,
};
},
});
Expand Down
8 changes: 6 additions & 2 deletions gui/src/components/request/body/TextBody.vue
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,14 @@ export default defineComponent({
showRenderedBody.value = true;
};
const viewRawBody = () => (showRenderedBody.value = false);
const copy = () =>
copyTextToClipboard(body.value).then(() =>
const copy = () => {
const valueToCopy = showRenderedBody.value
? renderedBody.value
: body.value;
copyTextToClipboard(valueToCopy).then(() =>
success(resources.requestBodyCopiedToClipboard)
);
};
const showMoreClick = () => {
showMoreClicked.value = true;
};
Expand Down
3 changes: 3 additions & 0 deletions gui/src/constants/technical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ export const imageMimeTypes = [
];

export const pdfMimeType = "application/pdf";

// UI constants
export const requestsPerPage = 15;
1 change: 1 addition & 0 deletions gui/src/domain/settings-model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface SettingsModel {
darkTheme: boolean;
saveSearchFilters: boolean;
requestPageSize: number;
}
16 changes: 14 additions & 2 deletions gui/src/store/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@ export const useRequestsStore = defineStore({
state: () => ({}),
getters: {},
actions: {
getRequestsOverview(): Promise<RequestOverviewModel[]> {
return get("/ph-api/requests/overview")
getRequestsOverview(
fromIdentifier?: string,
itemsPerPage?: number
): Promise<RequestOverviewModel[]> {
const headers: any = {};
if (itemsPerPage && Math.max(itemsPerPage, 0)) {
headers["x-items-per-page"] = itemsPerPage + 1;
}
if (fromIdentifier) {
headers["x-from-identifier"] = fromIdentifier;
}
return get("/ph-api/requests/overview", {
headers,
})
.then((response) => Promise.resolve(response))
.catch((error) => Promise.reject(error));
},
Expand Down
24 changes: 19 additions & 5 deletions gui/src/store/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,42 @@ import { defineStore } from "pinia";
import { getSettings, setSettings } from "@/utils/session";
import { browserUsesDarkTheme } from "@/utils/theme";
import type { SettingsModel } from "@/domain/settings-model";
import { requestsPerPage } from "@/constants/technical";

type GeneralState = {
settings: SettingsModel;
};

const savedSettings: SettingsModel = getSettings() || {
darkTheme: browserUsesDarkTheme(),
saveSearchFilters: true,
const savedSettings = getSettings();
const settings: SettingsModel = {
darkTheme:
savedSettings?.darkTheme !== undefined
? savedSettings.darkTheme
: browserUsesDarkTheme(),
saveSearchFilters:
savedSettings?.saveSearchFilters !== undefined
? savedSettings.saveSearchFilters
: true,
requestPageSize:
savedSettings?.requestPageSize !== undefined
? savedSettings.requestPageSize
: requestsPerPage,
};
export const useSettingsStore = defineStore({
id: "settings",
state: () =>
({
settings: <SettingsModel>{
darkTheme: savedSettings.darkTheme,
saveSearchFilters: savedSettings.saveSearchFilters,
darkTheme: settings.darkTheme,
saveSearchFilters: settings.saveSearchFilters,
requestPageSize: settings.requestPageSize,
},
} as GeneralState),
getters: {
getSettings: (state): SettingsModel => state.settings,
getDarkTheme: (state): boolean => state.settings.darkTheme,
getSaveSearchFilters: (state): boolean => state.settings.saveSearchFilters,
getRequestsPageSize: (state): number => state.settings.requestPageSize,
},
actions: {
storeSettings(settings: SettingsModel): void {
Expand Down
Loading