Compare commits

...

120 Commits

Author SHA1 Message Date
jamesgeorge007
4c74d0f865 chore: cleanup 2024-05-23 14:20:42 +05:30
jamesgeorge007
8b930a6d3d fix: resolve edge cases about moving collections under its sibling
- Increase test coverage.
- Move store mock data under `__tests__/__mocks__`.
- Rephrase test descriptions.
2024-05-22 21:00:49 +05:30
jamesgeorge007
f4a37f19c9 fix: empty state primary action for root collections
Ensure child collections are created instead of the action resulting in new root collections.
2024-05-22 21:00:49 +05:30
jamesgeorge007
86b17e2bd3 test: add test suite for personal workspace provider service 2024-05-22 21:00:49 +05:30
jamesgeorge007
e0083aa70d fix: handle based updates post collection move to a sibling level collection below it 2024-05-22 21:00:49 +05:30
jamesgeorge007
25b0818016 refactor: update inherited properties for affected requests flow updates 2024-05-22 21:00:46 +05:30
jamesgeorge007
648cc8f5bd refactor: handle based updates for affected requests post collection deletion 2024-05-22 20:59:59 +05:30
jamesgeorge007
6032cbb17b refactor: handle based updates for affected requests post request deletion 2024-05-22 20:59:59 +05:30
jamesgeorge007
52bff8ee68 refactor: handle based updates post collection reorder 2024-05-22 20:59:59 +05:30
jamesgeorge007
e342e53db1 refactor: handle based updates post collection move 2024-05-22 20:59:58 +05:30
jamesgeorge007
141652ffb6 fix: affected request indices computation post request reorder 2024-05-22 20:59:38 +05:30
jamesgeorge007
bb57b2248c fix: affected request indices computation post request move 2024-05-22 20:59:21 +05:30
jamesgeorge007
5d8da5fe49 chore: resolve type errors 2024-05-22 20:59:21 +05:30
Andrew Bastin
c8f0142b16 refactor: move more things to handles instead of handleref 2024-05-22 20:59:20 +05:30
jamesgeorge007
2f2273ee2c refactor: move to inert handles 2024-05-22 20:58:28 +05:30
jamesgeorge007
b239b6b4a6 refactor: handle updates post move request action
- Filter out duplicate issued handle entries.
- Move from `getAffectedIndexes` helper function to a custom implementation for updating affected request indices.
2024-05-22 20:57:48 +05:30
jamesgeorge007
bbac317b71 refactor: move to handle based updates with request move action 2024-05-22 20:57:48 +05:30
jamesgeorge007
412daa4d17 refactor: tab saveContext resolution post collection remove action 2024-05-22 20:57:48 +05:30
jamesgeorge007
0abdc63f0e refactor: better tab dirty check
Mark the tab (saved request under a collection) as not dirty if the request contents are reset to the value since previous save.
2024-05-22 20:57:47 +05:30
jamesgeorge007
9e8112a4e5 fix: make close all tabs action account for tabs with invalid request handles 2024-05-22 20:56:47 +05:30
jamesgeorge007
5aa57fce3f refactor: add save context resolution logic post request deletion 2024-05-22 20:56:47 +05:30
jamesgeorge007
8b65090dfb refactor: persist only request handles under tab saveContext at runtime
Remove provider, workspace and request IDs.
2024-05-22 20:56:47 +05:30
jamesgeorge007
7ca94a99b7 refactor: move tab saveContext resolution associated with actions on collections to be based on request handles 2024-05-22 20:56:46 +05:30
jamesgeorge007
fe3adeeb17 refactor: consider request handles with tab saveContext resolution for collection move/reorder actions 2024-05-22 20:56:17 +05:30
jamesgeorge007
3a195711a4 refactor: update data under request handles during tab save context resolution 2024-05-22 20:56:17 +05:30
jamesgeorge007
6cde6200ae refactor: signify updates via handle reference mutation post request move action 2024-05-22 20:56:17 +05:30
jamesgeorge007
e5e1260632 fix: ensure request name updates reflect immediately on the tabs 2024-05-22 20:56:17 +05:30
jamesgeorge007
90c9f2a9b1 fix: make writable handle operate on refs within the createRESTRequest method
Wrap the request handle data in a `ref` and make the writable handle operate over it ensuring reactive updates are received.
2024-05-22 20:56:17 +05:30
jamesgeorge007
db9ba17529 refactor: convey updates via handle mutation for update request action 2024-05-22 20:56:17 +05:30
jamesgeorge007
197d253e8b refactor: keep tab dirty status logic at the page level 2024-05-22 20:56:17 +05:30
jamesgeorge007
cd92dfec47 refactor: introduce writable handles to signify updates to handle references
A special list of writable handles is compiled in a list while issuing handles (request/collection creation, etc). Instead of manually computing the tab and toggling the dirty state, the writable handle is updated (changing the type to invalid on request deletion) and the tab with the request open can infer it via the update reflected in the request handle under the tab save context (reactive update trigger).
2024-05-22 20:53:49 +05:30
jamesgeorge007
8467417e7a refactor: persist request handles under tab saveContext
Only the IDs (workspace, provider & request IDs) to restore the handle are stored under `localStorage` and the handle is restored back at runtime.
2024-05-22 20:53:15 +05:30
jamesgeorge007
f6067f14aa fix: prevent infinite spinner state while expanding tree nodes 2024-05-22 20:52:57 +05:30
jamesgeorge007
3fd85df84b refactor: view implementation to retrieve collections for exporting 2024-05-22 20:52:57 +05:30
jamesgeorge007
01573cc51c chore: keep existing implementation for save context resolution 2024-05-22 20:52:57 +05:30
jamesgeorge007
b19486ea03 refactor: update provider method signatures + cleanup 2024-05-22 20:52:57 +05:30
jamesgeorge007
a729dfcacb fix: ensure tree nodes are not computed for requests 2024-05-22 20:52:57 +05:30
jamesgeorge007
62612e6c51 refactor: remove unnecessary safeguards + cleanup 2024-05-22 20:52:57 +05:30
jamesgeorge007
f87a4c81d8 refactor: leverage helpers 2024-05-22 20:52:57 +05:30
jamesgeorge007
116f2fd279 refactor: update provider method signatures 2024-05-22 20:52:57 +05:30
jamesgeorge007
7e2deaac5b fix: duplicate collection in search results
Ensure the entire collection tree is rendered if the search query matches a collection name.
2024-05-22 20:52:57 +05:30
jamesgeorge007
240b131e06 feat: support search at n level depth 2024-05-22 20:52:57 +05:30
jamesgeorge007
5b3986a53f fix: ensure the collection tree for search immediately reflects actions performed over it 2024-05-22 20:52:57 +05:30
jamesgeorge007
84fc31e8a9 refactor: port collection tree empty states 2024-05-22 20:52:56 +05:30
jamesgeorge007
c0978c3b20 refactor: eliminate parentCollectionID field from RESTCollectionViewRequest type
Collection ID serves the purpose.
2024-05-22 20:52:56 +05:30
jamesgeorge007
d70d5bdb16 refactor: eliminate collectionID from tab saveContext
Collection ID can be inferred from request ID by removing last index from the path.
2024-05-22 20:52:56 +05:30
jamesgeorge007
c30ee5becc refactor: session based search results view implementation 2024-05-22 20:50:32 +05:30
jamesgeorge007
5a64cdb7bc fix: update save context for affected requests with collection move/reorder 2024-05-22 20:50:32 +05:30
jamesgeorge007
89f2479845 refactor: integrate REST search collection adapter 2024-05-22 20:50:32 +05:30
jamesgeorge007
46654853f0 refactor: add new tree adapter corresponding to search 2024-05-22 20:50:32 +05:30
jamesgeorge007
af7e6b70cd refactor: view based implementation for search in personal workspace 2024-05-22 20:50:32 +05:30
jamesgeorge007
7c52c6b79d fix: associate requests under tabs while reordering collections 2024-05-22 20:50:32 +05:30
jamesgeorge007
fe01322bf7 refactor: integrate provider API methods for collection move/reorder 2024-05-22 20:50:32 +05:30
jamesgeorge007
0a0f441da1 refactor: unify markup 2024-05-22 20:50:32 +05:30
jamesgeorge007
d0c7c4a245 refactor: provider method definitions for collection reorder/move 2024-05-22 20:50:32 +05:30
jamesgeorge007
076006c4a6 refactor: port collection move/reorder 2024-05-22 20:50:32 +05:30
jamesgeorge007
6ed9c09f06 refactor: port import/export functionality 2024-05-22 20:50:31 +05:30
jamesgeorge007
8483339005 feat: add keypress actions 2024-05-22 20:49:49 +05:30
jamesgeorge007
cd23bb63c1 refactor: remove fields associated with pagination
Fix lint errors
2024-05-22 20:49:13 +05:30
James George
f4ea999d2d refactor: remove unnecessary imports and local state variable 2024-05-22 20:49:13 +05:30
jamesgeorge007
c43e4fcefd fix: open request straightaway after creating via save-as 2024-05-22 20:49:13 +05:30
jamesgeorge007
316dc8f759 refactor: persist IDs under tab save context 2024-05-22 20:49:12 +05:30
jamesgeorge007
89f7c2ce5e refactor: port share requests 2024-05-22 20:41:24 +05:30
jamesgeorge007
0d00826019 fix: ensure removing collection level headers persists 2024-05-22 20:41:24 +05:30
jamesgeorge007
00285df348 refactor: remove side effects from computed properties 2024-05-22 20:41:24 +05:30
jamesgeorge007
b821f452cf fix: ensure the reference is not kept while overwriting requests 2024-05-22 20:41:24 +05:30
jamesgeorge007
faa0bf7714 fix: updates to collection level authorization and headers reflect at the request level straightaway 2024-05-22 20:41:23 +05:30
jamesgeorge007
3a176f6620 fix: invalidate requests opened under tabs on deleting parent collection 2024-05-22 20:33:04 +05:30
jamesgeorge007
7549e456c8 fix: specify correct request index with update action 2024-05-22 20:33:04 +05:30
jamesgeorge007
68795a5017 refactor: port collection tree rendered in the save request modal to the new implementation 2024-05-22 20:33:03 +05:30
jamesgeorge007
b0c72fd295 feat: indicate request opened in the tab from the collection tree 2024-05-22 20:32:18 +05:30
jamesgeorge007
63eca80ff6 fix: prevent the need for an explicit save while editing request name 2024-05-22 20:32:18 +05:30
jamesgeorge007
30b6a67505 fix: prevent duplicate request creation and invalidate tabs with request deletion 2024-05-22 20:32:18 +05:30
jamesgeorge007
97899ec023 chore: cleanup 2024-05-22 20:32:18 +05:30
jamesgeorge007
2c47a63ca0 refactor: update provider method signature 2024-05-22 20:32:18 +05:30
jamesgeorge007
a0e373a4f3 refactor: persist request handle under tab saveContext
Bump vue version.
2024-05-22 20:32:16 +05:30
jamesgeorge007
392b2fc48d refactor: updates based on the provider methods signature changes 2024-05-22 20:30:55 +05:30
jamesgeorge007
c1a8a871d2 refactor: save request handle in tabs and remove tabs related logic from personal provider definition 2024-05-22 20:28:56 +05:30
jamesgeorge007
1abbdb0fe0 refactor: consistent return formats 2024-05-22 20:21:43 +05:30
jamesgeorge007
f0f504d10e refactor: unify edit collection API methods and ensure consistent naming convention 2024-05-22 20:21:42 +05:30
jamesgeorge007
f0dab55c99 refactor: prevent storing entire collection data in the respective handle 2024-05-22 20:21:15 +05:30
jamesgeorge007
d6a8e60239 refactor: finalize API methods 2024-05-22 20:21:14 +05:30
jamesgeorge007
89bcc58de6 refactor: compile data in handles
Introduce a handle for requests.
2024-05-22 20:16:54 +05:30
jamesgeorge007
ab7df212c2 refactor: iterations 2024-05-22 20:11:39 +05:30
jamesgeorge007
29e25b0ead refactor: initial iterations
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2024-05-22 20:10:50 +05:30
Nivedin
f8ac6dfeb1 chore: add workspace switcher login A/B testing flow (#4053)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2024-05-10 16:35:42 +05:30
Andrew Bastin
7d2d335b37 chore: revert back default interceptor for sh app to browser 2024-05-10 16:13:51 +05:30
Andrew Bastin
76875db865 chore: bump selfhost-desktop lockfile version 2024-05-10 15:04:16 +05:30
Balu Babu
96e2d87b57 feat: update node version to node20-apline3.19 (#4040) 2024-05-10 14:24:34 +05:30
islamzeki
be353d9f72 chore(i18n): update tr.json (#4039) 2024-05-10 14:16:04 +05:30
Nivedin
38bc2c12c3 refactor: set default interceptor to "proxy" (#4051) 2024-05-10 14:01:43 +05:30
Stéfany Larissa
97644fa508 chore: update pt-br translations (#4003) 2024-05-10 13:56:58 +05:30
Dmitry
eb3446ae23 locale: update ru locale (#4005)
Co-authored-by: Dmitry Mukovkin <d.mukovkin@cft.ru>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
Co-authored-by: Joel Jacob Stephen <70131076+JoelJacobStephen@users.noreply.github.com>
2024-05-10 13:51:25 +05:30
Dmitry
6c29961d09 Added new button to save request modal (#3976)
Co-authored-by: Dmitry Mukovkin <d.mukovkin@cft.ru>
Co-authored-by: Nivedin <53208152+nivedin@users.noreply.github.com>
2024-05-09 20:52:22 +05:30
Nivedin
ef1117d8cc refactor: switch workspace after creation (#4015)
Co-authored-by: amk-dev <akash.k.mohan98@gmail.com>
2024-05-09 20:28:24 +05:30
Balu Babu
5c4b651aee feat: healthcheck for external services (#4048)
* chore: installed terminus package and scaffolded the health module

* feat: added healthcheck for database with prisma

* chore: renamed label from prisma to database in health check route

* chore: reverted target of hopp-old-backend service to prod
2024-05-07 20:05:17 +05:30
Andrew Bastin
391e5a20f5 chore: bump versions to 2024.3.3 2024-05-06 22:57:33 +05:30
Andrew Bastin
4b8f3bd8da fix: code generate modal erroring out (#4033) 2024-05-06 22:44:13 +05:30
Joel Jacob Stephen
94248076e6 refactor(sh-admin): improved handling of server configurations in admin dashboard (#3971)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-05-06 21:50:31 +05:30
Nivedin
eecc3db4e9 chore: update placeholder text (#4023) 2024-04-30 16:49:32 +05:30
Andrew Bastin
426e7594f4 fix: tab systems erroring out due to out of sync tabOrdering and tabMap 2024-04-29 20:32:27 +05:30
Andrew Bastin
934dc473f0 chore: bump version to 2024.3.2 2024-04-29 19:21:48 +05:30
Andrew Bastin
be57255bf7 refactor: update to dioc v3 (#4009)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-04-29 19:06:18 +05:30
Balu Babu
f89561da54 fix: resolved mailer module email issue (#4000) 2024-04-29 12:05:07 +05:30
Joel Jacob Stephen
c2c4e620c2 fix(common): rest and graphql pages not being rendered when french is selected as app language (#4004)
Co-authored-by: nivedin <nivedinp@gmail.com>
2024-04-25 18:01:46 +05:30
Andrew Bastin
844eee0fa4 chore: update @hoppscotch/cli README 2024-04-22 21:12:27 +05:30
Andrew Bastin
d21bb65511 chore: bump cli version to 0.8 2024-04-22 20:29:03 +05:30
Andrew Bastin
4f614f7257 chore: bump version to 2024.3.1 2024-04-22 20:26:25 +05:30
Nivedin
0e2887b4e9 feat: first time user spotlight animation (#3977) 2024-04-22 12:21:30 +05:30
Andrew Bastin
18652ce400 fix: update prod.Dockerfile to add additional required deps during base build 2024-04-20 01:54:34 +05:30
Eduardo San Martin Morote
08c655235d fix: use pressed key rather than its code (#3978)
Co-authored-by: Joel Jacob Stephen <70131076+JoelJacobStephen@users.noreply.github.com>
2024-04-19 22:35:13 +05:30
Vincent M
51412549e8 chore(i18n): french lang translation (#3986) 2024-04-19 21:21:15 +05:30
James George
22c6eabd13 chore: migrate Node.js implementation for js-sandbox to isolated-vm (#3973)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2024-04-19 21:08:46 +05:30
Mir Arif Hasan
a079e0f04b refactor: infra-config code refactor (#3982)
* chore: add getMissingInfraConfigEntries to avoid code duplication

* feat: isServiceConfigured modified

* docs: add missing function doc

* feat: argon2 version updated and removed types-argon2 depricated package

* Revert "feat: argon2 version updated and removed types-argon2 depricated package"

This reverts commit b99f3c5aae.

* Revert "feat: isServiceConfigured modified"

This reverts commit eaa6f105bb.
2024-04-19 12:43:43 +05:30
jamesgeorge007
375d53263a test: fix failing tests 2024-04-16 23:55:07 +05:30
Thomas Bonnet
57ef3e085f chore(i18n): Updating the packages/hoppscotch-common/locales/fr.json file. (#3555) 2024-04-16 18:15:38 +05:30
Timotej
9fb6e59e36 fix(desktop): set window caption color if Windows version >= 11 (#3939) 2024-04-16 17:51:26 +05:30
Sawako
1b0802b0e6 fix: spanish lang translation messages (#3950) 2024-04-16 17:47:10 +05:30
krisztianbarat
fb45fe4627 chore(i18n): update locale hu (#3875) 2024-04-16 17:45:55 +05:30
Akash K
0f592d1789 fix: use proper values for addTo field when auth type is api-key (#3966) 2024-04-16 17:44:28 +05:30
205 changed files with 30298 additions and 16145 deletions

View File

@@ -17,22 +17,21 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Setup environment - name: Setup environment
run: mv .env.example .env run: mv .env.example .env
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v2.2.4 uses: pnpm/action-setup@v3
with: with:
version: 8 version: 8
run_install: true run_install: true
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache: pnpm
- name: Run tests - name: Run tests
run: pnpm test run: pnpm test

View File

@@ -34,11 +34,11 @@
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"vue": "3.3.9" "vue": "3.4.27"
}, },
"packageExtensions": { "packageExtensions": {
"httpsnippet@3.0.1": { "httpsnippet@3.0.1": {
"peerDependencies": { "dependencies": {
"ajv": "6.12.3" "ajv": "6.12.3"
} }
} }

View File

@@ -1,4 +1,4 @@
FROM node:18.8.0 AS builder FROM node:20.12.2 AS builder
WORKDIR /usr/src/app WORKDIR /usr/src/app

View File

@@ -3,9 +3,7 @@
"collection": "@nestjs/schematics", "collection": "@nestjs/schematics",
"sourceRoot": "src", "sourceRoot": "src",
"compilerOptions": { "compilerOptions": {
"assets": [ "assets": [{ "include": "mailer/templates/**/*", "outDir": "dist" }],
"**/*.hbs"
],
"watchAssets": true "watchAssets": true
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "hoppscotch-backend", "name": "hoppscotch-backend",
"version": "2024.3.0", "version": "2024.3.3",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,
@@ -35,6 +35,7 @@
"@nestjs/passport": "10.0.2", "@nestjs/passport": "10.0.2",
"@nestjs/platform-express": "10.2.7", "@nestjs/platform-express": "10.2.7",
"@nestjs/schedule": "4.0.1", "@nestjs/schedule": "4.0.1",
"@nestjs/terminus": "10.2.3",
"@nestjs/throttler": "5.0.1", "@nestjs/throttler": "5.0.1",
"@prisma/client": "5.8.1", "@prisma/client": "5.8.1",
"argon2": "0.30.3", "argon2": "0.30.3",
@@ -121,4 +122,4 @@
"^src/(.*)$": "<rootDir>/$1" "^src/(.*)$": "<rootDir>/$1"
} }
} }
} }

View File

@@ -26,6 +26,7 @@ import { loadInfraConfiguration } from './infra-config/helper';
import { MailerModule } from './mailer/mailer.module'; import { MailerModule } from './mailer/mailer.module';
import { PosthogModule } from './posthog/posthog.module'; import { PosthogModule } from './posthog/posthog.module';
import { ScheduleModule } from '@nestjs/schedule'; import { ScheduleModule } from '@nestjs/schedule';
import { HealthModule } from './health/health.module';
@Module({ @Module({
imports: [ imports: [
@@ -100,6 +101,7 @@ import { ScheduleModule } from '@nestjs/schedule';
InfraConfigModule, InfraConfigModule,
PosthogModule, PosthogModule,
ScheduleModule.forRoot(), ScheduleModule.forRoot(),
HealthModule,
], ],
providers: [GQLComplexityPlugin], providers: [GQLComplexityPlugin],
controllers: [AppController], controllers: [AppController],

View File

@@ -0,0 +1,24 @@
import { Controller, Get } from '@nestjs/common';
import {
HealthCheck,
HealthCheckService,
PrismaHealthIndicator,
} from '@nestjs/terminus';
import { PrismaService } from 'src/prisma/prisma.service';
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private prismaHealth: PrismaHealthIndicator,
private prisma: PrismaService,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
async () => this.prismaHealth.pingCheck('database', this.prisma),
]);
}
}

View File

@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
import { PrismaModule } from 'src/prisma/prisma.module';
import { TerminusModule } from '@nestjs/terminus';
@Module({
imports: [PrismaModule, TerminusModule],
controllers: [HealthController],
})
export class HealthModule {}

View File

@@ -156,6 +156,25 @@ export async function getDefaultInfraConfigs(): Promise<
return infraConfigDefaultObjs; return infraConfigDefaultObjs;
} }
/**
* Get the missing entries in the 'infra_config' table
* @returns Array of InfraConfig
*/
export async function getMissingInfraConfigEntries() {
const prisma = new PrismaService();
const [dbInfraConfigs, infraConfigDefaultObjs] = await Promise.all([
prisma.infraConfig.findMany(),
getDefaultInfraConfigs(),
]);
const missingEntries = infraConfigDefaultObjs.filter(
(config) =>
!dbInfraConfigs.some((dbConfig) => dbConfig.name === config.name),
);
return missingEntries;
}
/** /**
* Verify if 'infra_config' table is loaded with all entries * Verify if 'infra_config' table is loaded with all entries
* @returns boolean * @returns boolean
@@ -163,12 +182,7 @@ export async function getDefaultInfraConfigs(): Promise<
export async function isInfraConfigTablePopulated(): Promise<boolean> { export async function isInfraConfigTablePopulated(): Promise<boolean> {
const prisma = new PrismaService(); const prisma = new PrismaService();
try { try {
const dbInfraConfigs = await prisma.infraConfig.findMany(); const propsRemainingToInsert = await getMissingInfraConfigEntries();
const infraConfigDefaultObjs = await getDefaultInfraConfigs();
const propsRemainingToInsert = infraConfigDefaultObjs.filter(
(p) => !dbInfraConfigs.find((e) => e.name === p.name),
);
if (propsRemainingToInsert.length > 0) { if (propsRemainingToInsert.length > 0) {
console.log( console.log(

View File

@@ -21,7 +21,12 @@ import {
validateUrl, validateUrl,
} from 'src/utils'; } from 'src/utils';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { ServiceStatus, getDefaultInfraConfigs, stopApp } from './helper'; import {
ServiceStatus,
getDefaultInfraConfigs,
getMissingInfraConfigEntries,
stopApp,
} from './helper';
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args'; import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
@@ -56,14 +61,7 @@ export class InfraConfigService implements OnModuleInit {
*/ */
async initializeInfraConfigTable() { async initializeInfraConfigTable() {
try { try {
// Fetch the default values (value in .env) for configs to be saved in 'infra_config' table const propsToInsert = await getMissingInfraConfigEntries();
const infraConfigDefaultObjs = await getDefaultInfraConfigs();
// Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table
const dbInfraConfigs = await this.prisma.infraConfig.findMany();
const propsToInsert = infraConfigDefaultObjs.filter(
(p) => !dbInfraConfigs.find((e) => e.name === p.name),
);
if (propsToInsert.length > 0) { if (propsToInsert.length > 0) {
await this.prisma.infraConfig.createMany({ data: propsToInsert }); await this.prisma.infraConfig.createMany({ data: propsToInsert });
@@ -285,6 +283,7 @@ export class InfraConfigService implements OnModuleInit {
/** /**
* Get InfraConfigs by names * Get InfraConfigs by names
* @param names Names of the InfraConfigs * @param names Names of the InfraConfigs
* @param checkDisallowedKeys If true, check if the names are allowed to fetch by client
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async getMany(names: InfraConfigEnum[], checkDisallowedKeys: boolean = true) { async getMany(names: InfraConfigEnum[], checkDisallowedKeys: boolean = true) {

View File

@@ -52,11 +52,34 @@ hopp [options or commands] arguments
Taking the above example, `pw.env.get("ENV1")` will return `"value1"` Taking the above example, `pw.env.get("ENV1")` will return `"value1"`
## Install ## Install
- Before you install Hoppscotch CLI you need to make sure you have the dependencies it requires to run.
- **Windows & macOS**: You will need `node-gyp` installed. Find instructions here: https://github.com/nodejs/node-gyp
- **Debian/Ubuntu derivatives**:
```sh
sudo apt-get install python g++ build-essential
```
- **Alpine Linux**:
```sh
sudo apk add python3 make g++
```
- **Amazon Linux (AMI)**
```sh
sudo yum install gcc72 gcc72-c++
```
- **Arch Linux**
```sh
sudo pacman -S make gcc python
```
- **RHEL/Fedora derivatives**:
```sh
sudo dnf install python3 make gcc gcc-c++ zlib-devel brotli-devel openssl-devel libuv-devel
```
Install [@hoppscotch/cli](https://www.npmjs.com/package/@hoppscotch/cli) from npm by running:
``` - Once the dependencies are installed, install [@hoppscotch/cli](https://www.npmjs.com/package/@hoppscotch/cli) from npm by running:
npm i -g @hoppscotch/cli ```
``` npm i -g @hoppscotch/cli
```
## **Developing:** ## **Developing:**

View File

@@ -1,6 +1,31 @@
#!/usr/bin/env node #!/usr/bin/env node
// * The entry point of the CLI // * The entry point of the CLI
// @ts-check
import { cli } from "../dist/index.js"; import { cli } from "../dist/index.js";
cli(process.argv); import { spawnSync } from "child_process";
import { cloneDeep } from "lodash-es";
const nodeVersion = parseInt(process.versions.node.split(".")[0]);
// As per isolated-vm documentation, we need to supply `--no-node-snapshot` for node >= 20
// src: https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
if (nodeVersion >= 20 && !process.execArgv.includes("--no-node-snapshot")) {
const argCopy = cloneDeep(process.argv);
// Replace first argument with --no-node-snapshot
// We can get argv[0] from process.argv0
argCopy[0] = "--no-node-snapshot";
const result = spawnSync(
process.argv0,
argCopy,
{ stdio: "inherit" }
);
// Exit with the same status code as the spawned process
process.exit(result.status ?? 0);
} else {
cli(process.argv);
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@hoppscotch/cli", "name": "@hoppscotch/cli",
"version": "0.7.0", "version": "0.8.0",
"description": "A CLI to run Hoppscotch test scripts in CI environments.", "description": "A CLI to run Hoppscotch test scripts in CI environments.",
"homepage": "https://hoppscotch.io", "homepage": "https://hoppscotch.io",
"type": "module", "type": "module",
@@ -44,6 +44,7 @@
"axios": "1.6.7", "axios": "1.6.7",
"chalk": "5.3.0", "chalk": "5.3.0",
"commander": "11.1.0", "commander": "11.1.0",
"isolated-vm": "4.7.2",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"qs": "6.11.2", "qs": "6.11.2",
"verzod": "0.2.2", "verzod": "0.2.2",
@@ -65,4 +66,4 @@
"tsup": "8.0.2", "tsup": "8.0.2",
"typescript": "5.3.3" "typescript": "5.3.3"
} }
} }

View File

@@ -224,7 +224,7 @@ describe("Test `hopp test <file> --env <file>` command:", () => {
}); });
describe("Secret environment variables", () => { describe("Secret environment variables", () => {
jest.setTimeout(10000); jest.setTimeout(100000);
// Reads secret environment values from system environment // Reads secret environment values from system environment
test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => { test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => {

View File

@@ -1,27 +1,55 @@
{ {
"v": 1, "v": 1,
"name": "coll-v1", "name": "coll-v1",
"folders": [], "folders": [
"requests": [ {
"v": 1,
"name": "coll-v1-child",
"folders": [],
"requests": [
{ {
"url": "https://httpbin.org", "url": "https://echo.hoppscotch.io",
"path": "/get", "path": "/get",
"headers": [ "headers": [
{ "key": "Inactive-Header", "value": "Inactive Header", "active": false }, { "key": "Inactive-Header", "value": "Inactive Header", "active": false },
{ "key": "Authorization", "value": "Bearer token123", "active": true } { "key": "Authorization", "value": "Bearer token123", "active": true }
], ],
"params": [ "params": [
{ "key": "key", "value": "value", "active": true }, { "key": "key", "value": "value", "active": true },
{ "key": "inactive-key", "value": "inactive-param", "active": false } { "key": "inactive-key", "value": "inactive-param", "active": false }
], ],
"name": "req-v0", "name": "req-v0-II",
"method": "GET", "method": "GET",
"preRequestScript": "", "preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"contentType": "application/json", "contentType": "application/json",
"body": "", "body": "",
"auth": "Bearer Token", "auth": "Bearer Token",
"bearerToken": "token123" "bearerToken": "token123"
} }
] ]
} }
],
"requests": [
{
"url": "https://echo.hoppscotch.io",
"path": "/get",
"headers": [
{ "key": "Inactive-Header", "value": "Inactive Header", "active": false },
{ "key": "Authorization", "value": "Bearer token123", "active": true }
],
"params": [
{ "key": "key", "value": "value", "active": true },
{ "key": "inactive-key", "value": "inactive-param", "active": false }
],
"name": "req-v0",
"method": "GET",
"preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"contentType": "application/json",
"body": "",
"auth": "Bearer Token",
"bearerToken": "token123"
}
]
}

View File

@@ -1,11 +1,60 @@
{ {
"v": 1, "v": 1,
"name": "coll-v1", "name": "coll-v1",
"folders": [], "folders": [
{
"v": 1,
"name": "coll-v1-child",
"folders": [],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"headers": [
{
"key": "Inactive-Header",
"value": "Inactive Header",
"active": false
},
{
"key": "Authorization",
"value": "Bearer token123",
"active": true
}
],
"params": [
{
"key": "key",
"value": "value",
"active": true
},
{
"key": "inactive-key",
"value": "inactive-param",
"active": false
}
],
"name": "req-v1-II",
"method": "GET",
"preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"body": {
"contentType": null,
"body": null
},
"auth": {
"authType": "bearer",
"authActive": true,
"token": "token123"
}
}
]
}
],
"requests": [ "requests": [
{ {
"v": "1", "v": "1",
"endpoint": "https://httpbin.org/get", "endpoint": "https://echo.hoppscotch.io",
"headers": [ "headers": [
{ {
"key": "Inactive-Header", "key": "Inactive-Header",
@@ -33,7 +82,7 @@
"name": "req-v1", "name": "req-v1",
"method": "GET", "method": "GET",
"preRequestScript": "", "preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"body": { "body": {
"contentType": null, "contentType": null,
"body": null "body": null

View File

@@ -1,11 +1,66 @@
{ {
"v": 2, "v": 2,
"name": "coll-v2", "name": "coll-v2",
"folders": [], "folders": [
{
"v": 2,
"name": "coll-v2-child",
"folders": [],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"headers": [
{
"key": "Inactive-Header",
"value": "Inactive Header",
"active": false
},
{
"key": "Authorization",
"value": "Bearer token123",
"active": true
}
],
"params": [
{
"key": "key",
"value": "value",
"active": true
},
{
"key": "inactive-key",
"value": "inactive-param",
"active": false
}
],
"name": "req-v2-II",
"method": "GET",
"preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"body": {
"contentType": null,
"body": null
},
"auth": {
"authType": "bearer",
"authActive": true,
"token": "token123"
},
"requestVariables": []
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [ "requests": [
{ {
"v": "2", "v": "2",
"endpoint": "https://httpbin.org/get", "endpoint": "https://echo.hoppscotch.io",
"headers": [ "headers": [
{ {
"key": "Inactive-Header", "key": "Inactive-Header",
@@ -33,7 +88,7 @@
"name": "req-v2", "name": "req-v2",
"method": "GET", "method": "GET",
"preRequestScript": "", "preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"body": { "body": {
"contentType": null, "contentType": null,
"body": null "body": null

View File

@@ -1,11 +1,66 @@
{ {
"v": 2, "v": 2,
"name": "coll-v2", "name": "coll-v2",
"folders": [], "folders": [
{
"v": 2,
"name": "coll-v2-child",
"folders": [],
"requests": [
{
"v": "3",
"endpoint": "https://echo.hoppscotch.io",
"headers": [
{
"key": "Inactive-Header",
"value": "Inactive Header",
"active": false
},
{
"key": "Authorization",
"value": "Bearer token123",
"active": true
}
],
"params": [
{
"key": "key",
"value": "value",
"active": true
},
{
"key": "inactive-key",
"value": "inactive-param",
"active": false
}
],
"name": "req-v3-II",
"method": "GET",
"preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"body": {
"contentType": null,
"body": null
},
"auth": {
"authType": "bearer",
"authActive": true,
"token": "token123"
},
"requestVariables": []
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [ "requests": [
{ {
"v": "3", "v": "3",
"endpoint": "https://httpbin.org/get", "endpoint": "https://echo.hoppscotch.io",
"headers": [ "headers": [
{ {
"key": "Inactive-Header", "key": "Inactive-Header",
@@ -33,7 +88,7 @@
"name": "req-v3", "name": "req-v3",
"method": "GET", "method": "GET",
"preRequestScript": "", "preRequestScript": "",
"testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"Inactive-Header\"]).toBe(undefined)\n})", "testScript": "pw.test(\"Asserts request params\", () => {\n pw.expect(pw.response.body.args.key).toBe(\"value\")\n pw.expect(pw.response.body.args[\"inactive-key\"]).toBe(undefined)\n})\n\npw.test(\"Asserts request headers\", () => {\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer token123\")\n pw.expect(pw.response.body.headers[\"inactive-header\"]).toBe(undefined)\n})",
"body": { "body": {
"contentType": null, "contentType": null,
"body": null "body": null

View File

@@ -75,7 +75,7 @@
"auth": { "auth": {
"authType": "api-key", "authType": "api-key",
"authActive": true, "authActive": true,
"addTo": "Headers", "addTo": "HEADERS",
"key": "key", "key": "key",
"value": "test-key" "value": "test-key"
}, },

View File

@@ -5,8 +5,14 @@
"requests": [ "requests": [
{ {
"v": "3", "v": "3",
"auth": { "authType": "none", "authActive": true }, "auth": {
"body": { "body": null, "contentType": null }, "authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-headers", "name": "test-secret-headers",
"method": "GET", "method": "GET",
"params": [], "params": [],
@@ -18,13 +24,16 @@
} }
], ],
"requestVariables": [], "requestVariables": [],
"endpoint": "<<baseURL>>/headers", "endpoint": "<<echoHoppBaseURL>>/headers",
"testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.get(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.get(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})", "testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.get(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"secret-header-key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.get(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})",
"preRequestScript": "const secretHeaderValueFromPreReqScript = pw.env.get(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)" "preRequestScript": "const secretHeaderValueFromPreReqScript = pw.env.get(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
}, },
{ {
"v": "3", "v": "3",
"auth": { "authType": "none", "authActive": true }, "auth": {
"authType": "none",
"authActive": true
},
"body": { "body": {
"body": "{\n \"secretBodyKey\": \"<<secretBodyValue>>\"\n}", "body": "{\n \"secretBodyKey\": \"<<secretBodyValue>>\"\n}",
"contentType": "application/json" "contentType": "application/json"
@@ -34,14 +43,20 @@
"params": [], "params": [],
"headers": [], "headers": [],
"requestVariables": [], "requestVariables": [],
"endpoint": "<<baseURL>>/post", "endpoint": "<<echoHoppBaseURL>>/post",
"testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})", "testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(JSON.parse(pw.response.body.data).secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})",
"preRequestScript": "const secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)" "preRequestScript": "const secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)"
}, },
{ {
"v": "3", "v": "3",
"auth": { "authType": "none", "authActive": true }, "auth": {
"body": { "body": null, "contentType": null }, "authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-query-params", "name": "test-secret-query-params",
"method": "GET", "method": "GET",
"params": [ "params": [
@@ -53,7 +68,7 @@
], ],
"headers": [], "headers": [],
"requestVariables": [], "requestVariables": [],
"endpoint": "<<baseURL>>/get", "endpoint": "<<echoHoppBaseURL>>",
"testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})", "testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})",
"preRequestScript": "const secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)" "preRequestScript": "const secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)"
}, },
@@ -65,14 +80,17 @@
"username": "<<secretBasicAuthUsername>>", "username": "<<secretBasicAuthUsername>>",
"authActive": true "authActive": true
}, },
"body": { "body": null, "contentType": null }, "body": {
"body": null,
"contentType": null
},
"name": "test-secret-basic-auth", "name": "test-secret-basic-auth",
"method": "GET", "method": "GET",
"params": [], "params": [],
"headers": [], "headers": [],
"requestVariables": [], "requestVariables": [],
"endpoint": "<<baseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>", "endpoint": "<<httpbinBaseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>",
"testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});", "testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n // The endpoint at times results in a `502` bad gateway\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});",
"preRequestScript": "" "preRequestScript": ""
}, },
{ {
@@ -84,30 +102,42 @@
"username": "testuser", "username": "testuser",
"authActive": true "authActive": true
}, },
"body": { "body": null, "contentType": null }, "body": {
"body": null,
"contentType": null
},
"name": "test-secret-bearer-auth", "name": "test-secret-bearer-auth",
"method": "GET", "method": "GET",
"params": [], "params": [],
"headers": [], "headers": [],
"requestVariables": [], "requestVariables": [],
"endpoint": "<<baseURL>>/bearer", "endpoint": "<<httpbinBaseURL>>/bearer",
"testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.get(\"secretBearerToken\")\n const preReqSecretBearerToken = pw.env.get(\"preReqSecretBearerToken\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});", "testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.get(\"secretBearerToken\")\n const preReqSecretBearerToken = pw.env.get(\"preReqSecretBearerToken\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n // Safeguard to prevent test failures due to the endpoint\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});",
"preRequestScript": "const secretBearerToken = pw.env.get(\"secretBearerToken\")\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)" "preRequestScript": "const secretBearerToken = pw.env.get(\"secretBearerToken\")\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)"
}, },
{ {
"v": "3", "v": "3",
"auth": { "authType": "none", "authActive": true }, "auth": {
"body": { "body": null, "contentType": null }, "authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-fallback", "name": "test-secret-fallback",
"method": "GET", "method": "GET",
"params": [], "params": [],
"headers": [], "headers": [],
"requestVariables": [], "requestVariables": [],
"endpoint": "<<baseURL>>", "endpoint": "<<echoHoppBaseURL>>",
"testScript": "pw.test(\"Returns an empty string if the value for a secret environment variable is not found in the system environment\", () => {\n pw.expect(pw.env.get(\"nonExistentValueInSystemEnv\")).toBe(\"\")\n})", "testScript": "pw.test(\"Returns an empty string if the value for a secret environment variable is not found in the system environment\", () => {\n pw.expect(pw.env.get(\"nonExistentValueInSystemEnv\")).toBe(\"\")\n})",
"preRequestScript": "" "preRequestScript": ""
} }
], ],
"auth": { "authType": "inherit", "authActive": false }, "auth": {
"authType": "inherit",
"authActive": false
},
"headers": [] "headers": []
} }

View File

@@ -1,6 +1,6 @@
{ {
"v": 2, "v": 2,
"name": "secret-envs-setters-coll", "name": "secret-envs-persistence-coll",
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
@@ -24,8 +24,8 @@
"active": true "active": true
} }
], ],
"endpoint": "<<baseURL>>/headers", "endpoint": "<<echoHoppBaseURL>>",
"testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})", "testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"secret-header-key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})",
"preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)" "preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
}, },
{ {
@@ -49,8 +49,8 @@
"active": true "active": true
} }
], ],
"endpoint": "<<baseURL>>/headers", "endpoint": "<<echoHoppBaseURL>>",
"testScript": "pw.test(\"Value set at the pre-request script takes precedence\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value-overriden\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value-overriden\")\n})", "testScript": "pw.test(\"Value set at the pre-request script takes precedence\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value-overriden\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"secret-header-key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value-overriden\")\n})",
"preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value-overriden\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)" "preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value-overriden\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
}, },
{ {
@@ -68,8 +68,8 @@
"params": [], "params": [],
"requestVariables": [], "requestVariables": [],
"headers": [], "headers": [],
"endpoint": "<<baseURL>>/post", "endpoint": "<<echoHoppBaseURL>>/post",
"testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})", "testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(JSON.parse(pw.response.body.data).secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})",
"preRequestScript": "const secretBodyValue = pw.env.get(\"secretBodyValue\")\n\nif (!secretBodyValue) { \n pw.env.set(\"secretBodyValue\", \"secret-body-value\")\n}\n\nconst secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)" "preRequestScript": "const secretBodyValue = pw.env.get(\"secretBodyValue\")\n\nif (!secretBodyValue) { \n pw.env.set(\"secretBodyValue\", \"secret-body-value\")\n}\n\nconst secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)"
}, },
{ {
@@ -93,7 +93,7 @@
], ],
"requestVariables": [], "requestVariables": [],
"headers": [], "headers": [],
"endpoint": "<<baseURL>>/get", "endpoint": "<<echoHoppBaseURL>>",
"testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})", "testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})",
"preRequestScript": "const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n\nif (!secretQueryParamValue) {\n pw.env.set(\"secretQueryParamValue\", \"secret-query-param-value\")\n}\n\nconst secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)" "preRequestScript": "const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n\nif (!secretQueryParamValue) {\n pw.env.set(\"secretQueryParamValue\", \"secret-query-param-value\")\n}\n\nconst secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)"
}, },
@@ -114,8 +114,8 @@
"params": [], "params": [],
"requestVariables": [], "requestVariables": [],
"headers": [], "headers": [],
"endpoint": "<<baseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>", "endpoint": "<<httpbinBaseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>",
"testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});", "testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n // The endpoint at times results in a `502` bad gateway\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});",
"preRequestScript": "let secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n\nlet secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\nif (!secretBasicAuthUsername) {\n pw.env.set(\"secretBasicAuthUsername\", \"test-user\")\n}\n\nif (!secretBasicAuthPassword) {\n pw.env.set(\"secretBasicAuthPassword\", \"test-pass\")\n}" "preRequestScript": "let secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n\nlet secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\nif (!secretBasicAuthUsername) {\n pw.env.set(\"secretBasicAuthUsername\", \"test-user\")\n}\n\nif (!secretBasicAuthPassword) {\n pw.env.set(\"secretBasicAuthPassword\", \"test-pass\")\n}"
}, },
{ {
@@ -136,8 +136,8 @@
"params": [], "params": [],
"requestVariables": [], "requestVariables": [],
"headers": [], "headers": [],
"endpoint": "<<baseURL>>/bearer", "endpoint": "<<httpbinBaseURL>>/bearer",
"testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n const preReqSecretBearerToken = pw.env.resolve(\"<<preReqSecretBearerToken>>\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});", "testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n const preReqSecretBearerToken = pw.env.resolve(\"<<preReqSecretBearerToken>>\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n // Safeguard to prevent test failures due to the endpoint\n if (pw.response.status !== 200) {\n return\n }\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});",
"preRequestScript": "let secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n\nif (!secretBearerToken) {\n pw.env.set(\"secretBearerToken\", \"test-token\")\n secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n}\n\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)" "preRequestScript": "let secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n\nif (!secretBearerToken) {\n pw.env.set(\"secretBearerToken\", \"test-token\")\n secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n}\n\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)"
} }
], ],
@@ -146,4 +146,4 @@
"authActive": false "authActive": false
}, },
"headers": [] "headers": []
} }

View File

@@ -5,7 +5,7 @@
"requests": [ "requests": [
{ {
"v": "3", "v": "3",
"endpoint": "https://httpbin.org/post", "endpoint": "https://echo.hoppscotch.io/post",
"name": "req", "name": "req",
"params": [], "params": [],
"headers": [ "headers": [
@@ -18,7 +18,7 @@
"method": "POST", "method": "POST",
"auth": { "authType": "none", "authActive": true }, "auth": { "authType": "none", "authActive": true },
"preRequestScript": "pw.env.set(\"preReqVarOne\", \"pre-req-value-one\")\n\npw.env.set(\"preReqVarTwo\", \"pre-req-value-two\")\n\npw.env.set(\"customHeaderValueFromSecretVar\", \"custom-header-secret-value\")\n\npw.env.set(\"customBodyValue\", \"custom-body-value\")", "preRequestScript": "pw.env.set(\"preReqVarOne\", \"pre-req-value-one\")\n\npw.env.set(\"preReqVarTwo\", \"pre-req-value-two\")\n\npw.env.set(\"customHeaderValueFromSecretVar\", \"custom-header-secret-value\")\n\npw.env.set(\"customBodyValue\", \"custom-body-value\")",
"testScript": "pw.test(\"Secret environment value set from the pre-request script takes precedence\", () => {\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(\"pre-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the pre-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request headers that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"custom-header-secret-value\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request body that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.json.key).toBe(\"custom-body-value\")\n})\n\npw.test(\"Secret environment variable set from the post-request script takes precedence\", () => {\n pw.env.set(\"postReqVarOne\", \"post-req-value-one\")\n pw.expect(pw.env.get(\"postReqVarOne\")).toBe(\"post-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the post-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully removes environment variables via the pw.env.unset method\", () => {\n pw.env.unset(\"preReqVarOne\")\n pw.env.unset(\"postReqVarTwo\")\n\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(undefined)\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(undefined)\n})", "testScript": "pw.test(\"Secret environment value set from the pre-request script takes precedence\", () => {\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(\"pre-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the pre-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request headers that are set in pre-request script\", () => {\n pw.expect(pw.response.body.headers[\"custom-header\"]).toBe(\"custom-header-secret-value\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request body that are set in pre-request script\", () => {\n pw.expect(JSON.parse(pw.response.body.data).key).toBe(\"custom-body-value\")\n})\n\npw.test(\"Secret environment variable set from the post-request script takes precedence\", () => {\n pw.env.set(\"postReqVarOne\", \"post-req-value-one\")\n pw.expect(pw.env.get(\"postReqVarOne\")).toBe(\"post-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the post-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully removes environment variables via the pw.env.unset method\", () => {\n pw.env.unset(\"preReqVarOne\")\n pw.env.unset(\"postReqVarTwo\")\n\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(undefined)\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(undefined)\n})",
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n \"key\": \"<<customBodyValue>>\"\n}" "body": "{\n \"key\": \"<<customBodyValue>>\"\n}"

View File

@@ -32,7 +32,12 @@
"secret": true "secret": true
}, },
{ {
"key": "baseURL", "key": "echoHoppBaseURL",
"value": "https://echo.hoppscotch.io",
"secret": false
},
{
"key": "httpbinBaseURL",
"value": "https://httpbin.org", "value": "https://httpbin.org",
"secret": false "secret": false
} }

View File

@@ -38,7 +38,12 @@
"secret": true "secret": true
}, },
{ {
"key": "baseURL", "key": "echoHoppBaseURL",
"value": "https://echo.hoppscotch.io",
"secret": false
},
{
"key": "httpbinBaseURL",
"value": "https://httpbin.org", "value": "https://httpbin.org",
"secret": false "secret": false
} }

View File

@@ -3,15 +3,16 @@ import { resolve } from "path";
import { ExecResponse } from "./types"; import { ExecResponse } from "./types";
export const runCLI = (args: string, options = {}): Promise<ExecResponse> => export const runCLI = (args: string, options = {}): Promise<ExecResponse> => {
{ const CLI_PATH = resolve(__dirname, "../../bin/hopp.js");
const CLI_PATH = resolve(__dirname, "../../bin/hopp"); const command = `node ${CLI_PATH} ${args}`;
const command = `node ${CLI_PATH} ${args}`
return new Promise((resolve) => return new Promise((resolve) =>
exec(command, options, (error, stdout, stderr) => resolve({ error, stdout, stderr })) exec(command, options, (error, stdout, stderr) =>
); resolve({ error, stdout, stderr })
} )
);
};
export const trimAnsi = (target: string) => { export const trimAnsi = (target: string) => {
const ansiRegex = const ansiRegex =
@@ -25,12 +26,18 @@ export const getErrorCode = (out: string) => {
return ansiTrimmedStr.split(" ")[0]; return ansiTrimmedStr.split(" ")[0];
}; };
export const getTestJsonFilePath = (file: string, kind: "collection" | "environment") => { export const getTestJsonFilePath = (
file: string,
kind: "collection" | "environment"
) => {
const kindDir = { const kindDir = {
collection: "collections", collection: "collections",
environment: "environments", environment: "environments",
}[kind]; }[kind];
const filePath = resolve(__dirname, `../../src/__tests__/samples/${kindDir}/${file}`); const filePath = resolve(
__dirname,
`../../src/__tests__/samples/${kindDir}/${file}`
);
return filePath; return filePath;
}; };

View File

@@ -1,6 +1,7 @@
import chalk from "chalk"; import chalk from "chalk";
import { Command } from "commander"; import { Command } from "commander";
import * as E from "fp-ts/Either"; import * as E from "fp-ts/Either";
import { version } from "../package.json"; import { version } from "../package.json";
import { test } from "./commands/test"; import { test } from "./commands/test";
import { handleError } from "./handlers/error"; import { handleError } from "./handlers/error";
@@ -20,7 +21,7 @@ const CLI_AFTER_ALL_TXT = `\nFor more help, head on to ${accent(
"https://docs.hoppscotch.io/documentation/clients/cli" "https://docs.hoppscotch.io/documentation/clients/cli"
)}`; )}`;
const program = new Command() const program = new Command();
program program
.name("hopp") .name("hopp")

View File

@@ -7,6 +7,41 @@ import { error } from "../types/errors";
import { FormDataEntry } from "../types/request"; import { FormDataEntry } from "../types/request";
import { isHoppErrnoException } from "./checks"; import { isHoppErrnoException } from "./checks";
const getValidRequests = (
collections: HoppCollection[],
collectionFilePath: string
) => {
return collections.map((collection) => {
// Validate requests using zod schema
const requestSchemaParsedResult = z
.array(entityReference(HoppRESTRequest))
.safeParse(collection.requests);
// Handle validation errors
if (!requestSchemaParsedResult.success) {
throw error({
code: "MALFORMED_COLLECTION",
path: collectionFilePath,
data: "Please check the collection data.",
});
}
// Recursively validate requests in nested folders
if (collection.folders.length > 0) {
collection.folders = getValidRequests(
collection.folders,
collectionFilePath
);
}
// Return validated collection
return {
...collection,
requests: requestSchemaParsedResult.data,
};
});
};
/** /**
* Parses array of FormDataEntry to FormData. * Parses array of FormDataEntry to FormData.
* @param values Array of FormDataEntry. * @param values Array of FormDataEntry.
@@ -82,22 +117,5 @@ export async function parseCollectionData(
}); });
} }
return collectionSchemaParsedResult.data.map((collection) => { return getValidRequests(collectionSchemaParsedResult.data, path);
const requestSchemaParsedResult = z
.array(entityReference(HoppRESTRequest))
.safeParse(collection.requests);
if (!requestSchemaParsedResult.success) {
throw error({
code: "MALFORMED_COLLECTION",
path,
data: "Please check the collection data.",
});
}
return {
...collection,
requests: requestSchemaParsedResult.data,
};
});
} }

View File

@@ -136,13 +136,13 @@ export function getEffectiveRESTRequest(
} }
} else if (request.auth.authType === "api-key") { } else if (request.auth.authType === "api-key") {
const { key, value, addTo } = request.auth; const { key, value, addTo } = request.auth;
if (addTo === "Headers") { if (addTo === "HEADERS") {
effectiveFinalHeaders.push({ effectiveFinalHeaders.push({
active: true, active: true,
key: parseTemplateString(key, envVariables), key: parseTemplateString(key, envVariables),
value: parseTemplateString(value, envVariables), value: parseTemplateString(value, envVariables),
}); });
} else if (addTo === "Query params") { } else if (addTo === "QUERY_PARAMS") {
effectiveFinalParams.push({ effectiveFinalParams.push({
active: true, active: true,
key: parseTemplateString(key, envVariables), key: parseTemplateString(key, envVariables),

View File

@@ -162,6 +162,8 @@
"label_client_credentials": "Client Credentials" "label_client_credentials": "Client Credentials"
}, },
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"pass_by_query_params_label": "Query Parameters",
"pass_by_headers_label": "Headers",
"password": "Password", "password": "Password",
"save_to_inherit": "Please save this request in any collection to inherit the authorization", "save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
@@ -173,6 +175,7 @@
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Edit Collection", "edit": "Edit Collection",
"import_or_create": "Import or create a collection", "import_or_create": "Import or create a collection",
"import_collection":"Import Collection",
"invalid_name": "Please provide a name for the collection", "invalid_name": "Please provide a name for the collection",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -371,7 +374,8 @@
"mutations": "Mutations", "mutations": "Mutations",
"schema": "Schema", "schema": "Schema",
"subscriptions": "Subscriptions", "subscriptions": "Subscriptions",
"switch_connection": "Switch connection" "switch_connection": "Switch connection",
"url_placeholder": "Enter a GraphQL endpoint URL"
}, },
"graphql_collections": { "graphql_collections": {
"title": "GraphQL Collections" "title": "GraphQL Collections"
@@ -595,6 +599,7 @@
"title": "Request", "title": "Request",
"type": "Request type", "type": "Request type",
"url": "URL", "url": "URL",
"url_placeholder": "Enter a URL or paste a cURL command",
"variables": "Variables", "variables": "Variables",
"view_my_links": "View my links" "view_my_links": "View my links"
}, },
@@ -847,6 +852,13 @@
"new": "Create new workspace", "new": "Create new workspace",
"switch_to_personal": "Switch to your personal workspace", "switch_to_personal": "Switch to your personal workspace",
"title": "Workspaces" "title": "Workspaces"
},
"phrases":{
"try": "Try",
"import_collections": "Import collections",
"create_environment": "Create environment",
"create_workspace": "Create workspace",
"share_request": "Share request"
} }
}, },
"sse": { "sse": {
@@ -1015,7 +1027,8 @@
"personal": "Personal Workspace", "personal": "Personal Workspace",
"other_workspaces": "My Workspaces", "other_workspaces": "My Workspaces",
"team": "Workspace", "team": "Workspace",
"title": "Workspaces" "title": "Workspaces",
"no_workspace": "No Workspace"
}, },
"site_protection": { "site_protection": {
"login_to_continue": "Login to continue", "login_to_continue": "Login to continue",

View File

@@ -32,8 +32,8 @@
"no": "No", "no": "No",
"open_workspace": "Abrir espacio de trabajo", "open_workspace": "Abrir espacio de trabajo",
"paste": "Pegar", "paste": "Pegar",
"prettify": "Embellecer", "prettify": "Formatear",
"properties": "Properties", "properties": "Propiedades",
"remove": "Eliminar", "remove": "Eliminar",
"rename": "Rename", "rename": "Rename",
"restore": "Restaurar", "restore": "Restaurar",
@@ -63,7 +63,7 @@
"contact_us": "Contáctanos", "contact_us": "Contáctanos",
"cookies": "Cookies", "cookies": "Cookies",
"copy": "Copiar", "copy": "Copiar",
"copy_interface_type": "Copy interface type", "copy_interface_type": "Copiar tipo de interfaz",
"copy_user_id": "Copiar token de autenticación de usuario", "copy_user_id": "Copiar token de autenticación de usuario",
"developer_option": "Opciones para desarrolladores", "developer_option": "Opciones para desarrolladores",
"developer_option_description": "Herramientas para desarrolladores que ayudan en el desarrollo y mantenimiento de Hoppscotch.", "developer_option_description": "Herramientas para desarrolladores que ayudan en el desarrollo y mantenimiento de Hoppscotch.",
@@ -80,14 +80,14 @@
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Se ha encontrado una nueva versión. Recarga la página para usarla.", "new_version_found": "Se ha encontrado una nueva versión. Recarga la página para usarla.",
"open_in_hoppscotch": "Open in Hoppscotch", "open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Opciones",
"proxy_privacy_policy": "Política de privacidad de proxy", "proxy_privacy_policy": "Política de privacidad de proxy",
"reload": "Recargar", "reload": "Recargar",
"search": "Buscar", "search": "Buscar",
"share": "Compartir", "share": "Compartir",
"shortcuts": "Atajos", "shortcuts": "Atajos",
"social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", "social_description": "Síguenos en redes sociales para estar al día de las últimas noticias, actualizaciones y lanzamientos.",
"social_links": "Social links", "social_links": "Redes sociales",
"spotlight": "Destacar", "spotlight": "Destacar",
"status": "Estado", "status": "Estado",
"status_description": "Comprobar el estado del sitio web", "status_description": "Comprobar el estado del sitio web",
@@ -119,27 +119,27 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generar token", "generate_token": "Generar token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Las cabeceras de autorización se envían como parte de la carga útil de connection_init",
"include_in_url": "Incluir en la URL", "include_in_url": "Incluir en la URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "inherited_from": "Heredado {auth} de colección padre {collection} ",
"learn": "Aprender", "learn": "Aprender",
"oauth": { "oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "El servidor de autenticación ha devuelto un estado de error",
"redirect_auth_token_request_failed": "Request to get the auth token failed", "redirect_auth_token_request_failed": "Fallo en la solicitud de token de autentificación",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token", "redirect_auth_token_request_invalid_response": "Respuesta no válida del punto final de Token al solicitar un token de autentificación",
"redirect_invalid_state": "Invalid State value present in the redirect", "redirect_invalid_state": "Valor de estado no válido presente en la redirección",
"redirect_no_auth_code": "No Authorization Code present in the redirect", "redirect_no_auth_code": "No hay código de autorización en la redirección",
"redirect_no_client_id": "No Client ID defined", "redirect_no_client_id": "No se ha definido el ID de cliente",
"redirect_no_client_secret": "No Client Secret Defined", "redirect_no_client_secret": "No se ha definido ningún ID secreto de cliente",
"redirect_no_code_verifier": "No Code Verifier Defined", "redirect_no_code_verifier": "No se ha definido ningún verificador de códigos",
"redirect_no_token_endpoint": "No Token Endpoint Defined", "redirect_no_token_endpoint": "No se ha definido ningún punto final de token",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_oauth_redirect": "Algo ha ido mal durante la redirección OAuth",
"something_went_wrong_on_token_generation": "Something went wrong on token generation", "something_went_wrong_on_token_generation": "Algo salió mal en la generación del token",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" "token_generation_oidc_discovery_failed": "Fallo en la generación del token: OpenID Connect Discovery Failed"
}, },
"pass_key_by": "Pasar por", "pass_key_by": "Pasar por",
"password": "Contraseña", "password": "Contraseña",
"save_to_inherit": "Please save this request in any collection to inherit the authorization", "save_to_inherit": "Por favor, guarda esta solicitud en cualquier colección para heredar la autorización",
"token": "Token", "token": "Token",
"type": "Tipo de autorización", "type": "Tipo de autorización",
"username": "Nombre de usuario" "username": "Nombre de usuario"
@@ -148,7 +148,7 @@
"created": "Colección creada", "created": "Colección creada",
"different_parent": "No se puede reordenar la colección con un padre diferente", "different_parent": "No se puede reordenar la colección con un padre diferente",
"edit": "Editar colección", "edit": "Editar colección",
"import_or_create": "Import or create a collection", "import_or_create": "Importar o crear una colección",
"invalid_name": "Proporciona un nombre válido para la colección.", "invalid_name": "Proporciona un nombre válido para la colección.",
"invalid_root_move": "La colección ya está en la raíz", "invalid_root_move": "La colección ya está en la raíz",
"moved": "Movido con éxito", "moved": "Movido con éxito",
@@ -157,20 +157,20 @@
"name_length_insufficient": "El nombre de la colección debe tener al menos 3 caracteres", "name_length_insufficient": "El nombre de la colección debe tener al menos 3 caracteres",
"new": "Nueva colección", "new": "Nueva colección",
"order_changed": "Orden de colección actualizada", "order_changed": "Orden de colección actualizada",
"properties": "Collection Properties", "properties": "Propiedades de la colección",
"properties_updated": "Collection Properties Updated", "properties_updated": "Propiedades de la colección actualizadas",
"renamed": "Colección renombrada", "renamed": "Colección renombrada",
"request_in_use": "Solicitud en uso", "request_in_use": "Solicitud en uso",
"save_as": "Guardar como", "save_as": "Guardar como",
"save_to_collection": "Save to Collection", "save_to_collection": "Guardar en la colección",
"select": "Seleccionar colección", "select": "Seleccionar colección",
"select_location": "Seleccionar ubicación", "select_location": "Seleccionar ubicación",
"select_team": "Seleccionar equipo", "select_team": "Seleccionar equipo",
"team_collections": "Colecciones de equipos" "team_collections": "Colecciones de equipos"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "Are you sure you want to close this tab?", "close_unsaved_tab": "¿Seguro que quieres cerrar esta pestaña?",
"close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", "close_unsaved_tabs": "¿Estás seguro de que quieres cerrar todas las pestañas? {count} pestañas no guardadas se perderán.",
"exit_team": "¿Estás seguro de que quieres dejar este equipo?", "exit_team": "¿Estás seguro de que quieres dejar este equipo?",
"logout": "¿Estás seguro de que deseas cerrar la sesión?", "logout": "¿Estás seguro de que deseas cerrar la sesión?",
"remove_collection": "¿Estás seguro de que deseas eliminar esta colección de forma permanente?", "remove_collection": "¿Estás seguro de que deseas eliminar esta colección de forma permanente?",
@@ -178,7 +178,7 @@
"remove_folder": "¿Estás seguro de que deseas eliminar esta carpeta de forma permanente?", "remove_folder": "¿Estás seguro de que deseas eliminar esta carpeta de forma permanente?",
"remove_history": "¿Estás seguro de que deseas eliminar todo el historial de forma permanente?", "remove_history": "¿Estás seguro de que deseas eliminar todo el historial de forma permanente?",
"remove_request": "¿Estás seguro de que deseas eliminar esta solicitud de forma permanente?", "remove_request": "¿Estás seguro de que deseas eliminar esta solicitud de forma permanente?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?", "remove_shared_request": "¿Estás seguro de que quieres eliminar definitivamente esta solicitud compartida?",
"remove_team": "¿Estás seguro de que deseas eliminar este equipo?", "remove_team": "¿Estás seguro de que deseas eliminar este equipo?",
"remove_telemetry": "¿Estás seguro de que deseas darse de baja de la telemetría?", "remove_telemetry": "¿Estás seguro de que deseas darse de baja de la telemetría?",
"request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.", "request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.",
@@ -186,35 +186,35 @@
"sync": "¿Estás seguro de que deseas sincronizar este espacio de trabajo?" "sync": "¿Estás seguro de que deseas sincronizar este espacio de trabajo?"
}, },
"context_menu": { "context_menu": {
"add_parameters": "Add to parameters", "add_parameters": "Añadir a parámetros",
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Abrir solicitud en una nueva pestaña",
"set_environment_variable": "Set as variable" "set_environment_variable": "Establecer como variable"
}, },
"cookies": { "cookies": {
"modal": { "modal": {
"cookie_expires": "Expires", "cookie_expires": "Expira en",
"cookie_name": "Name", "cookie_name": "Nombre",
"cookie_path": "Path", "cookie_path": "Ruta",
"cookie_string": "Cookie string", "cookie_string": "Cookies",
"cookie_value": "Value", "cookie_value": "Valor",
"empty_domain": "Domain is empty", "empty_domain": "Dominio vacio",
"empty_domains": "Domain list is empty", "empty_domains": "No hay dominios",
"enter_cookie_string": "Enter cookie string", "enter_cookie_string": "Introducir cookies",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", "interceptor_no_support": "El interceptor seleccionado actualmente no admite cookies. Seleccione otro interceptor e inténtelo de nuevo.",
"managed_tab": "Managed", "managed_tab": "Gestionado",
"new_domain_name": "New domain name", "new_domain_name": "Nuevo nombre de dominio",
"no_cookies_in_domain": "No cookies set for this domain", "no_cookies_in_domain": "No hay cookies para este dominio",
"raw_tab": "Raw", "raw_tab": "Sin procesar",
"set": "Set a cookie" "set": "Establecer una cookie"
} }
}, },
"count": { "count": {
"header": "Encabezado {count}", "header": "{count} encabezado(s)",
"message": "Mensaje {count}", "message": "{count} mensaje(s)",
"parameter": "Parámetro {count}", "parameter": "{count} parámetro(s)",
"protocol": "Protocolo {count}", "protocol": "{count} protocolo(s)",
"value": "Valor {cuenta}", "value": "{cuenta} valor(es)",
"variable": "Variable {count}" "variable": "{count} variable(es)"
}, },
"documentation": { "documentation": {
"generate": "Generar documentación", "generate": "Generar documentación",
@@ -223,26 +223,26 @@
"empty": { "empty": {
"authorization": "Esta solicitud no utiliza ninguna autorización.", "authorization": "Esta solicitud no utiliza ninguna autorización.",
"body": "Esta solicitud no tiene cuerpo", "body": "Esta solicitud no tiene cuerpo",
"collection": "La colección está vacía", "collection": "Colección vacía",
"collections": "Las colecciones están vacías", "collections": "No hay colecciones",
"documentation": "Conectarse a un punto final de GraphQL para ver la documentación", "documentation": "Conectarse a un punto final de GraphQL para ver la documentación",
"endpoint": "El punto final no puede estar vacío", "endpoint": "El punto final no puede estar vacío",
"environments": "Los entornos están vacíos", "environments": "No hay entornos",
"folder": "La carpeta está vacía", "folder": "Carpeta vacía",
"headers": "Esta solicitud no tiene encabezados", "headers": "Esta solicitud no tiene encabezados",
"history": "El historial está vacío", "history": "No hay historial",
"invites": "La lista de invitados está vacía", "invites": "Lista de invitados vacía",
"members": "El equipo está vacío", "members": "No hay miembros en el equipo",
"parameters": "Esta solicitud no tiene ningún parámetro", "parameters": "Esta solicitud no tiene ningún parámetro",
"pending_invites": "No hay invitaciones pendientes para este equipo", "pending_invites": "No hay invitaciones pendientes para este equipo",
"profile": "Iniciar sesión para ver tu perfil", "profile": "Iniciar sesión para ver tu perfil",
"protocols": "Los protocolos están vacíos", "protocols": "No hay protocolos",
"schema": "Conectarse a un punto final de GraphQL", "schema": "Conectarse a un punto final de GraphQL",
"shared_requests": "Shared requests are empty", "shared_requests": "No hay solicitudes compartidas",
"shared_requests_logout": "Login to view your shared requests or create a new one", "shared_requests_logout": "Iniciar sesión para ver sus solicitudes compartidas o crear una nueva",
"subscription": "Subscriptions are empty", "subscription": "No hay suscripciones",
"team_name": "Nombre del equipo vacío", "team_name": "Nombre del equipo vacío",
"teams": "Los equipos están vacíos", "teams": "No hay equipos",
"tests": "No hay pruebas para esta solicitud", "tests": "No hay pruebas para esta solicitud",
"shortcodes": "Aún no se han creado Shortcodes" "shortcodes": "Aún no se han creado Shortcodes"
}, },
@@ -250,41 +250,41 @@
"add_to_global": "Añadir a Global", "add_to_global": "Añadir a Global",
"added": "Adición al entorno", "added": "Adición al entorno",
"create_new": "Crear un nuevo entorno", "create_new": "Crear un nuevo entorno",
"created": "Environment created", "created": "Entorno creado",
"deleted": "Eliminar el entorno", "deleted": "Eliminar el entorno",
"duplicated": "Environment duplicated", "duplicated": "Entorno duplicado",
"edit": "Editar entorno", "edit": "Editar entorno",
"empty_variables": "No variables", "empty_variables": "No hay variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Variables globales",
"import_or_create": "Import or create a environment", "import_or_create": "Importar o crear un entorno",
"invalid_name": "Proporciona un nombre válido para el entorno.", "invalid_name": "Proporciona un nombre válido para el entorno.",
"list": "Environment variables", "list": "Variables de entorno",
"my_environments": "Mis entornos", "my_environments": "Mis entornos",
"name": "Name", "name": "Nombre",
"nested_overflow": "las variables de entorno anidadas están limitadas a 10 niveles", "nested_overflow": "las variables de entorno anidadas están limitadas a 10 niveles",
"new": "Nuevo entorno", "new": "Nuevo entorno",
"no_active_environment": "No active environment", "no_active_environment": "Ningún entorno activo",
"no_environment": "Sin entorno", "no_environment": "Sin entorno",
"no_environment_description": "No se ha seleccionado ningún entorno. Elije qué hacer con las siguientes variables.", "no_environment_description": "No se ha seleccionado ningún entorno. Elije qué hacer con las siguientes variables.",
"quick_peek": "Environment Quick Peek", "quick_peek": "Vistazo rápido al entorno",
"replace_with_variable": "Replace with variable", "replace_with_variable": "Sustituir por variable",
"scope": "Scope", "scope": "Ámbito",
"select": "Seleccionar entorno", "select": "Seleccionar entorno",
"set": "Set environment", "set": "Establecer entorno",
"set_as_environment": "Set as environment", "set_as_environment": "Establecer como entorno",
"team_environments": "Entornos de trabajo en equipo", "team_environments": "Entornos de trabajo en equipo",
"title": "Entornos", "title": "Entornos",
"updated": "Entorno actualizado", "updated": "Entorno actualizado",
"value": "Value", "value": "Valor",
"variable": "Variable", "variable": "Variable",
"variable_list": "Lista de variables" "variable_list": "Lista de variables"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers", "authproviders_load_error": "No se han podido cargar los proveedores de autenticación",
"browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.", "browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.",
"check_console_details": "Consulta el registro de la consola para obtener más detalles.", "check_console_details": "Consulta el registro de la consola para obtener más detalles.",
"check_how_to_add_origin": "Check how you can add an origin", "check_how_to_add_origin": "Comprueba cómo puede añadir un origen",
"curl_invalid_format": "cURL no está formateado correctamente", "curl_invalid_format": "cURL no está formateado correctamente",
"danger_zone": "Zona de peligro", "danger_zone": "Zona de peligro",
"delete_account": "Tu cuenta es actualmente propietaria en estos equipos:", "delete_account": "Tu cuenta es actualmente propietaria en estos equipos:",
@@ -300,13 +300,13 @@
"json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo", "json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo",
"network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.", "network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.",
"network_fail": "No se pudo enviar la solicitud", "network_fail": "No se pudo enviar la solicitud",
"no_collections_to_export": "No collections to export. Please create a collection to get started.", "no_collections_to_export": "No hay colecciones para exportar. Crea una colección para empezar.",
"no_duration": "Sin duración", "no_duration": "Sin duración",
"no_environments_to_export": "No environments to export. Please create an environment to get started.", "no_environments_to_export": "No hay entornos para exportar. Por favor, crea un entorno para empezar.",
"no_results_found": "No se han encontrado coincidencias", "no_results_found": "No se han encontrado coincidencias",
"page_not_found": "No se ha podido encontrar esta página", "page_not_found": "No se ha podido encontrar esta página",
"please_install_extension": "Please install the extension and add origin to the extension.", "please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Error de proxy",
"script_fail": "No se pudo ejecutar el script de solicitud previa", "script_fail": "No se pudo ejecutar el script de solicitud previa",
"something_went_wrong": "Algo salió mal", "something_went_wrong": "Algo salió mal",
"test_script_fail": "No se ha podido ejecutar la secuencia de comandos posterior a la solicitud" "test_script_fail": "No se ha podido ejecutar la secuencia de comandos posterior a la solicitud"
@@ -314,15 +314,15 @@
"export": { "export": {
"as_json": "Exportar como JSON", "as_json": "Exportar como JSON",
"create_secret_gist": "Crear un Gist secreto", "create_secret_gist": "Crear un Gist secreto",
"failed": "Something went wrong while exporting", "failed": "Algo ha ido mal al exportar",
"gist_created": "Gist creado", "gist_created": "Gist creado",
"require_github": "Iniciar sesión con GitHub para crear un Gist secreto", "require_github": "Iniciar sesión con GitHub para crear un Gist secreto",
"title": "Exportar" "title": "Exportar"
}, },
"filter": { "filter": {
"all": "All", "all": "Todos",
"none": "None", "none": "Ninguno",
"starred": "Starred" "starred": "Destacado"
}, },
"folder": { "folder": {
"created": "Carpeta creada", "created": "Carpeta creada",
@@ -333,16 +333,16 @@
"renamed": "Carpeta renombrada" "renamed": "Carpeta renombrada"
}, },
"graphql": { "graphql": {
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", "connection_switch_confirm": "¿Deseas conectarte con el endpoint GraphQL más reciente?",
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", "connection_switch_new_url": "Al cambiar a una pestaña se desconectará de la conexión GraphQL activa. La nueva URL de conexión es",
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", "connection_switch_url": "Estás conectado a un endpoint GraphQL cuya URL de conexión es",
"mutations": "Mutaciones", "mutations": "Mutaciones",
"schema": "Esquema", "schema": "Esquema",
"subscriptions": "Suscripciones", "subscriptions": "Suscripciones",
"switch_connection": "Switch connection" "switch_connection": "Cambiar conexión"
}, },
"graphql_collections": { "graphql_collections": {
"title": "GraphQL Collections" "title": "Colecciones de GraphQL"
}, },
"group": { "group": {
"time": "Tiempo", "time": "Tiempo",
@@ -355,8 +355,8 @@
}, },
"helpers": { "helpers": {
"authorization": "El encabezado de autorización se generará automáticamente cuando se envía la solicitud.", "authorization": "El encabezado de autorización se generará automáticamente cuando se envía la solicitud.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.", "collection_properties_authorization": " Esta autorización se establecerá para cada solicitud de esta colección.",
"collection_properties_header": "This header will be set for every request in this collection.", "collection_properties_header": "Este encabezado se establecerá para cada solicitud de esta colección.",
"generate_documentation_first": "Generar la documentación primero", "generate_documentation_first": "Generar la documentación primero",
"network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.", "network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.",
"offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.", "offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.",
@@ -376,10 +376,10 @@
"import": { "import": {
"collections": "Importar colecciones", "collections": "Importar colecciones",
"curl": "Importar cURL", "curl": "Importar cURL",
"environments_from_gist": "Import From Gist", "environments_from_gist": "Importar desde Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist", "environments_from_gist_description": "Importar entornos Hoppscotch desde Gist",
"failed": "Importación fallida", "failed": "Importación fallida",
"from_file": "Import from File", "from_file": "Importar desde archivo",
"from_gist": "Importar desde Gist", "from_gist": "Importar desde Gist",
"from_gist_description": "Importar desde URL de Gist", "from_gist_description": "Importar desde URL de Gist",
"from_insomnia": "Importar desde Insomnia", "from_insomnia": "Importar desde Insomnia",
@@ -394,41 +394,41 @@
"from_postman_description": "Importar desde una colección de Postman", "from_postman_description": "Importar desde una colección de Postman",
"from_url": "Importar desde una URL", "from_url": "Importar desde una URL",
"gist_url": "Introduce la URL de Gist", "gist_url": "Introduce la URL de Gist",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist", "gql_collections_from_gist_description": "Importar colecciones GraphQL desde Gist",
"hoppscotch_environment": "Hoppscotch Environment", "hoppscotch_environment": "Entorno de Hoppscotch",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", "hoppscotch_environment_description": "Importar archivo JSON del entorno de Hoppscotch",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "No se han podido obtener datos de la url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error al importar colecciones",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Tipo no admitido. Los valores aceptados son \"hoppscotch\", \"openapi\", \"postman\", \"insomnia\".",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Colecciones Importadas",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", "insomnia_environment_description": "Importar el entorno de Insomnia desde un archivo JSON/YAML",
"json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch", "json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch",
"postman_environment": "Postman Environment", "postman_environment": "Entorno de Postman",
"postman_environment_description": "Import Postman Environment from a JSON file", "postman_environment_description": "Importar entorno de Postman desde un archivo JSON",
"title": "Importar" "title": "Importar"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Inspeccionar posibles errores",
"environment": { "environment": {
"add_environment": "Add to Environment", "add_environment": "Añadir al Entorno",
"not_found": "Environment variable “{environment}” not found." "not_found": "No se ha encontrado la variable de entorno \"{environment}\"."
}, },
"header": { "header": {
"cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." "cookie": "El navegador no permite que Hoppscotch establezca el encabezado Cookie. Mientras trabajamos en la aplicación de escritorio de Hoppscotch (próximamente), por favor utilice el encabezado de autorización en su lugar."
}, },
"response": { "response": {
"401_error": "Please check your authentication credentials.", "401_error": "Compruebe tus credenciales de autenticación.",
"404_error": "Please check your request URL and method type.", "404_error": "Compruebe la URL de su solicitud y el tipo de método.",
"cors_error": "Please check your Cross-Origin Resource Sharing configuration.", "cors_error": "Por favor, comprueba tu configuración de Compartición de Recursos \"Cross-Origin\".",
"default_error": "Please check your request.", "default_error": "Por favor, comprueba tu solicitud.",
"network_error": "Please check your network connection." "network_error": "Comprueba tu conexión de red."
}, },
"title": "Inspector", "title": "Inspeccionador",
"url": { "url": {
"extension_not_installed": "Extension not installed.", "extension_not_installed": "Extensión no instalada.",
"extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", "extension_unknown_origin": "Asegúrate de haber agregado el origen del punto final de la API a la lista de Extensiones del Navegador Hoppscotch.",
"extention_enable_action": "Enable Browser Extension", "extention_enable_action": "Activar la extensión del navegador",
"extention_not_enabled": "Extension not enabled." "extention_not_enabled": "Extensión no habilitada."
} }
}, },
"layout": { "layout": {
@@ -442,10 +442,10 @@
"close_unsaved_tab": "Tienes cambios sin guardar", "close_unsaved_tab": "Tienes cambios sin guardar",
"collections": "Colecciones", "collections": "Colecciones",
"confirm": "Confirmar", "confirm": "Confirmar",
"customize_request": "Customize Request", "customize_request": "Personalizar solicitud",
"edit_request": "Editar solicitud", "edit_request": "Editar solicitud",
"import_export": "Importación y exportación", "import_export": "Importación y exportación",
"share_request": "Share Request" "share_request": "Compartir solicitud"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "Ya estás suscrito a este tema.", "already_subscribed": "Ya estás suscrito a este tema.",
@@ -493,7 +493,7 @@
}, },
"profile": { "profile": {
"app_settings": "Ajustes de la aplicación", "app_settings": "Ajustes de la aplicación",
"default_hopp_displayname": "Unnamed User", "default_hopp_displayname": "Usuario anónimo",
"editor": "Editor", "editor": "Editor",
"editor_description": "Los editores pueden añadir, editar y eliminar solicitudes.", "editor_description": "Los editores pueden añadir, editar y eliminar solicitudes.",
"email_verification_mail": "Se ha enviado un correo electrónico de verificación a tu dirección de correo electrónico. Haz clic en el enlace para verificar tu dirección de correo electrónico.", "email_verification_mail": "Se ha enviado un correo electrónico de verificación a tu dirección de correo electrónico. Haz clic en el enlace para verificar tu dirección de correo electrónico.",
@@ -526,12 +526,12 @@
"enter_curl": "Ingrese cURL", "enter_curl": "Ingrese cURL",
"generate_code": "Generar código", "generate_code": "Generar código",
"generated_code": "Código generado", "generated_code": "Código generado",
"go_to_authorization_tab": "Go to Authorization tab", "go_to_authorization_tab": "Ir a la pestaña Autorización",
"go_to_body_tab": "Go to Body tab", "go_to_body_tab": "Ir a la pestaña de cuerpo",
"header_list": "Lista de encabezados", "header_list": "Lista de encabezados",
"invalid_name": "Proporciona un nombre para la solicitud.", "invalid_name": "Proporciona un nombre para la solicitud.",
"method": "Método", "method": "Método",
"moved": "Request moved", "moved": "Solicitud movida",
"name": "Nombre de solicitud", "name": "Nombre de solicitud",
"new": "Nueva solicitud", "new": "Nueva solicitud",
"order_changed": "Orden de solicitudes actualizadas", "order_changed": "Orden de solicitudes actualizadas",
@@ -543,8 +543,8 @@
"path": "Ruta", "path": "Ruta",
"payload": "Carga útil", "payload": "Carga útil",
"query": "Consulta", "query": "Consulta",
"raw_body": "Cuerpo de solicitud sin procesar", "raw_body": "cuerpo sin procesar",
"rename": "Rename Request", "rename": "Renombrar solicitud",
"renamed": "Solicitud renombrada", "renamed": "Solicitud renombrada",
"run": "Ejecutar", "run": "Ejecutar",
"save": "Guardar", "save": "Guardar",
@@ -552,8 +552,8 @@
"saved": "Solicitud guardada", "saved": "Solicitud guardada",
"share": "Compartir", "share": "Compartir",
"share_description": "Comparte Hoppscotch con tus amigos", "share_description": "Comparte Hoppscotch con tus amigos",
"share_request": "Share Request", "share_request": "Compartir solicitud",
"stop": "Stop", "stop": "Detener",
"title": "Solicitud", "title": "Solicitud",
"type": "Tipo de solicitud", "type": "Tipo de solicitud",
"url": "URL", "url": "URL",
@@ -571,7 +571,7 @@
"json": "JSON", "json": "JSON",
"pdf": "PDF", "pdf": "PDF",
"preview_html": "Vista previa de HTML", "preview_html": "Vista previa de HTML",
"raw": "Crudo", "raw": "Sin procesar",
"size": "Tamaño", "size": "Tamaño",
"status": "Estado", "status": "Estado",
"time": "Tiempo", "time": "Tiempo",
@@ -635,29 +635,29 @@
"verify_email": "Verificar correo electrónico" "verify_email": "Verificar correo electrónico"
}, },
"shared_requests": { "shared_requests": {
"button": "Button", "button": "Botón",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "button_info": "Crea un botón \"Ejecutar en Hoppscotch\" para tu página web, blog o un README.",
"copy_html": "Copy HTML", "copy_html": "Copiar HTML",
"copy_link": "Copy Link", "copy_link": "Copiar enlace",
"copy_markdown": "Copy Markdown", "copy_markdown": "Copiar Markdown",
"creating_widget": "Creating widget", "creating_widget": "Crear widget",
"customize": "Customize", "customize": "Personalizar",
"deleted": "Shared request deleted", "deleted": "Solicitud compartida eliminada",
"description": "Select a widget, you can change and customize this later", "description": "Selecciona un widget, puedes cambiarlo y personalizarlo más tarde",
"embed": "Embed", "embed": "Incrustar",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", "embed_info": "Añada un mini \"Hoppscotch API Playground\" a tu sitio web, blog o documentación.",
"link": "Link", "link": "Enlace",
"link_info": "Create a shareable link to share with anyone on the internet with view access.", "link_info": "Crea un enlace compartible para compartirlo con cualquier persona en Internet con acceso de visualización.",
"modified": "Shared request modified", "modified": "Solicitud compartida modificada",
"not_found": "Shared request not found", "not_found": "Solicitud compartida no encontrada",
"open_new_tab": "Open in new tab", "open_new_tab": "Abrir en una nueva pestaña",
"preview": "Preview", "preview": "Vista previa",
"run_in_hoppscotch": "Run in Hoppscotch", "run_in_hoppscotch": "Correr en Hoppscotch",
"theme": { "theme": {
"dark": "Dark", "dark": "Oscuro",
"light": "Light", "light": "Claro",
"system": "System", "system": "Sistema",
"title": "Theme" "title": "Tema"
} }
}, },
"shortcut": { "shortcut": {
@@ -684,8 +684,8 @@
"title": "Navegación" "title": "Navegación"
}, },
"others": { "others": {
"prettify": "Prettify Editor's Content", "prettify": "Formatear el contenido del editor",
"title": "Others" "title": "Otros"
}, },
"request": { "request": {
"delete_method": "Seleccionar método DELETE", "delete_method": "Seleccionar método DELETE",
@@ -697,13 +697,13 @@
"post_method": "Seleccionar método POST", "post_method": "Seleccionar método POST",
"previous_method": "Seleccionar método anterior", "previous_method": "Seleccionar método anterior",
"put_method": "Seleccionar método PUT", "put_method": "Seleccionar método PUT",
"rename": "Rename Request", "rename": "Renombrar solicitud",
"reset_request": "Solicitud de reinicio", "reset_request": "Solicitud de reinicio",
"save_request": "Save Request", "save_request": "Guardar solicitud",
"save_to_collections": "Guardar en colecciones", "save_to_collections": "Guardar en colecciones",
"send_request": "Enviar solicitud", "send_request": "Enviar solicitud",
"share_request": "Share Request", "share_request": "Compartir solicitud",
"show_code": "Generate code snippet", "show_code": "Generar fragmento de código",
"title": "Solicitud", "title": "Solicitud",
"copy_request_link": "Copiar enlace de solicitud" "copy_request_link": "Copiar enlace de solicitud"
}, },
@@ -722,95 +722,95 @@
}, },
"show": { "show": {
"code": "Mostrar código", "code": "Mostrar código",
"collection": "Expand Collection Panel", "collection": "Ampliar el panel de colecciones",
"more": "Mostrar más", "more": "Mostrar más",
"sidebar": "Mostrar barra lateral" "sidebar": "Mostrar barra lateral"
}, },
"socketio": { "socketio": {
"communication": "Comunicación", "communication": "Comunicación",
"connection_not_authorized": "This SocketIO connection does not use any authentication.", "connection_not_authorized": "Esta conexión SocketIO no utiliza ningún tipo de autenticación.",
"event_name": "Nombre del evento", "event_name": "Nombre del evento",
"events": "Eventos", "events": "Eventos",
"log": "Registro", "log": "Registro",
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "Change Language", "change_language": "Cambiar idioma",
"environments": { "environments": {
"delete": "Delete current environment", "delete": "Borrar el entorno actual",
"duplicate": "Duplicate current environment", "duplicate": "Duplicar el entorno actual",
"duplicate_global": "Duplicate global environment", "duplicate_global": "Entorno global duplicado",
"edit": "Edit current environment", "edit": "Editar el entorno actual",
"edit_global": "Edit global environment", "edit_global": "Editar el entorno global",
"new": "Create new environment", "new": "Crear un nuevo entorno",
"new_variable": "Create a new environment variable", "new_variable": "Crear una nueva variable de entorno",
"title": "Environments" "title": "Entornos"
}, },
"general": { "general": {
"chat": "Chat with support", "chat": "Chatear con el servicio de asistencia",
"help_menu": "Help and support", "help_menu": "Ayuda y asistencia",
"open_docs": "Read Documentation", "open_docs": "Leer la documentación",
"open_github": "Open GitHub repository", "open_github": "Abrir repositorio de GitHub",
"open_keybindings": "Keyboard shortcuts", "open_keybindings": "Atajos de teclado",
"social": "Social", "social": "Social",
"title": "General" "title": "General"
}, },
"graphql": { "graphql": {
"connect": "Connect to server", "connect": "Conectarse al servidor",
"disconnect": "Disconnect from server" "disconnect": "Desconectarse del servidor"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "Invite your friends to Hoppscotch", "invite": "Invita a tus amigos a Hoppscotch",
"title": "Miscellaneous" "title": "Varios"
}, },
"request": { "request": {
"save_as_new": "Save as new request", "save_as_new": "Guardar como nueva solicitud",
"select_method": "Select method", "select_method": "Seleccionar método",
"switch_to": "Switch to", "switch_to": "Cambiar a",
"tab_authorization": "Authorization tab", "tab_authorization": "Pestaña de autorización",
"tab_body": "Body tab", "tab_body": "Pestaña de cuerpo",
"tab_headers": "Headers tab", "tab_headers": "Pestaña de encabezados",
"tab_parameters": "Parameters tab", "tab_parameters": "Pestaña de parámetros",
"tab_pre_request_script": "Pre-request script tab", "tab_pre_request_script": "Pestaña del script de pre-solicitud",
"tab_query": "Query tab", "tab_query": "Pestaña de consulta",
"tab_tests": "Tests tab", "tab_tests": "Pestaña de pruebas",
"tab_variables": "Variables tab" "tab_variables": "Pestaña de variables"
}, },
"response": { "response": {
"copy": "Copy response", "copy": "Copiar respuesta",
"download": "Download response as file", "download": "Descargar la respuesta como archivo",
"title": "Response" "title": "Respuesta"
}, },
"section": { "section": {
"interceptor": "Interceptor", "interceptor": "Interceptor",
"interface": "Interface", "interface": "Interfaz",
"theme": "Theme", "theme": "Tema",
"user": "User" "user": "Usuario"
}, },
"settings": { "settings": {
"change_interceptor": "Change Interceptor", "change_interceptor": "Cambiar Interceptor",
"change_language": "Change Language", "change_language": "Cambiar idioma",
"theme": { "theme": {
"black": "Black", "black": "Negro",
"dark": "Dark", "dark": "Oscuro",
"light": "Light", "light": "Claro",
"system": "System preference" "system": "Preferencia del sistema"
} }
}, },
"tab": { "tab": {
"close_current": "Close current tab", "close_current": "Cerrar la pestaña actual",
"close_others": "Close all other tabs", "close_others": "Cerrar todas las demás pestañas",
"duplicate": "Duplicate current tab", "duplicate": "Duplicar pestaña actual",
"new_tab": "Open a new tab", "new_tab": "Abrir una nueva pestaña",
"title": "Tabs" "title": "Pestañas"
}, },
"workspace": { "workspace": {
"delete": "Delete current team", "delete": "Borrar el equipo actual",
"edit": "Edit current team", "edit": "Editar el equipo actual",
"invite": "Invite people to team", "invite": "Invitar al equipo",
"new": "Create new team", "new": "Crear un nuevo equipo",
"switch_to_personal": "Switch to your personal workspace", "switch_to_personal": "Cambia a tu espacio de trabajo personal",
"title": "Teams" "title": "Equipos"
} }
}, },
"sse": { "sse": {
@@ -825,10 +825,10 @@
"connected": "Conectado", "connected": "Conectado",
"connected_to": "Conectado a {name}", "connected_to": "Conectado a {name}",
"connecting_to": "Conectando con {name}...", "connecting_to": "Conectando con {name}...",
"connection_error": "Failed to connect", "connection_error": "Error de conexión",
"connection_failed": "Error de conexión", "connection_failed": "Conexión fallida",
"connection_lost": "Conexión perdida", "connection_lost": "Conexión perdida",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard", "copied_interface_to_clipboard": "Copiado tipo de interfaz {language} al portapapeles",
"copied_to_clipboard": "Copiado al portapapeles", "copied_to_clipboard": "Copiado al portapapeles",
"deleted": "Eliminado", "deleted": "Eliminado",
"deprecated": "OBSOLETO", "deprecated": "OBSOLETO",
@@ -836,21 +836,21 @@
"disconnected": "Desconectado", "disconnected": "Desconectado",
"disconnected_from": "Desconectado de {name}", "disconnected_from": "Desconectado de {name}",
"docs_generated": "Documentación generada", "docs_generated": "Documentación generada",
"download_failed": "Download failed", "download_failed": "Descarga fallida",
"download_started": "Descarga iniciada", "download_started": "Descarga iniciada",
"enabled": "Activado", "enabled": "Activado",
"file_imported": "Archivo importado", "file_imported": "Archivo importado",
"finished_in": "Terminado en {duration} ms", "finished_in": "Terminado en {duration}ms",
"hide": "Hide", "hide": "Ocultar",
"history_deleted": "Historial eliminado", "history_deleted": "Historial eliminado",
"linewrap": "Envolver líneas", "linewrap": "Envolver líneas",
"loading": "Cargando...", "loading": "Cargando...",
"message_received": "Mensaje: {mensaje} llegó sobre el tema: {topic}", "message_received": "Mensaje: llegó {message} al: {topic}",
"mqtt_subscription_failed": "Algo ha ido mal al suscribirse al tema: {topic}", "mqtt_subscription_failed": "Algo ha ido mal al suscribirse al tema: {topic}",
"none": "Ninguno", "none": "Ninguno",
"nothing_found": "Nada encontrado para", "nothing_found": "Nada encontrado para",
"published_error": "Algo ha ido mal al publicar el mensaje: {topic} al tema: {message}", "published_error": "Algo ha ido mal al publicar el mensaje: {message} al tema: {topic}",
"published_message": "Mensaje publicado: {mensaje} al tema: {topic}", "published_message": "Mensaje publicado: {message} al tema: {topic}",
"reconnection_error": "Fallo en la reconexión", "reconnection_error": "Fallo en la reconexión",
"show": "Show", "show": "Show",
"subscribed_failed": "Error al suscribirse al tema: {topic}", "subscribed_failed": "Error al suscribirse al tema: {topic}",
@@ -874,12 +874,12 @@
"tab": { "tab": {
"authorization": "Autorización", "authorization": "Autorización",
"body": "Cuerpo", "body": "Cuerpo",
"close": "Close Tab", "close": "Cerrar pestaña",
"close_others": "Close other Tabs", "close_others": "Cerrar otras pestañas",
"collections": "Colecciones", "collections": "Colecciones",
"documentation": "Documentación", "documentation": "Documentación",
"duplicate": "Duplicate Tab", "duplicate": "Duplicar pestaña",
"environments": "Environments", "environments": "Entornos",
"headers": "Encabezados", "headers": "Encabezados",
"history": "Historial", "history": "Historial",
"mqtt": "MQTT", "mqtt": "MQTT",
@@ -888,7 +888,7 @@
"queries": "Consultas", "queries": "Consultas",
"query": "Consulta", "query": "Consulta",
"schema": "Esquema", "schema": "Esquema",
"shared_requests": "Shared Requests", "shared_requests": "Solicitudes compartidas",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Pruebas", "tests": "Pruebas",
@@ -905,7 +905,7 @@
"email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.", "email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.",
"exit": "Salir del equipo", "exit": "Salir del equipo",
"exit_disabled": "Solo el propietario puede salir del equipo", "exit_disabled": "Solo el propietario puede salir del equipo",
"failed_invites": "Failed invites", "failed_invites": "Invitaciones fallidas",
"invalid_coll_id": "Identificador de colección no válido", "invalid_coll_id": "Identificador de colección no válido",
"invalid_email_format": "El formato de correo electrónico no es válido", "invalid_email_format": "El formato de correo electrónico no es válido",
"invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.", "invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.",
@@ -915,7 +915,7 @@
"invite": "Invitar", "invite": "Invitar",
"invite_more": "Invitar a más", "invite_more": "Invitar a más",
"invite_tooltip": "Invite a personas a este espacio de trabajo", "invite_tooltip": "Invite a personas a este espacio de trabajo",
"invited_to_team": "{owner} te ha invitado a unirte al {tema}", "invited_to_team": "{owner} te ha invitado al equipo {team}",
"join": "Invitación aceptada", "join": "Invitación aceptada",
"join_beta": "Únete al programa beta para acceder a los equipos.", "join_beta": "Únete al programa beta para acceder a los equipos.",
"join_team": "Entrar a {team}", "join_team": "Entrar a {team}",
@@ -930,7 +930,7 @@
"member_removed": "Usuario eliminado", "member_removed": "Usuario eliminado",
"member_role_updated": "Funciones de usuario actualizadas", "member_role_updated": "Funciones de usuario actualizadas",
"members": "Miembros", "members": "Miembros",
"more_members": "+{count} more", "more_members": "+{count} más",
"name_length_insufficient": "El nombre del equipo debe tener al menos 6 caracteres", "name_length_insufficient": "El nombre del equipo debe tener al menos 6 caracteres",
"name_updated": "Nombre de equipo actualizado", "name_updated": "Nombre de equipo actualizado",
"new": "Nuevo equipo", "new": "Nuevo equipo",
@@ -944,13 +944,13 @@
"parent_coll_move": "No se puede mover la colección a una colección hija", "parent_coll_move": "No se puede mover la colección a una colección hija",
"pending_invites": "Invitaciones pendientes", "pending_invites": "Invitaciones pendientes",
"permissions": "Permisos", "permissions": "Permisos",
"same_target_destination": "Same target and destination", "same_target_destination": "Mismo objetivo y destino",
"saved": "Equipo guardado", "saved": "Equipo guardado",
"select_a_team": "Seleccionar un equipo", "select_a_team": "Seleccionar un equipo",
"success_invites": "Success invites", "success_invites": "Invitaciones con éxito",
"title": "Equipos", "title": "Equipos",
"we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!", "we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!",
"we_sent_invite_link_description": "Pide a todos los invitados que revisen tu bandeja de entrada. Haz clic en el enlace para unirse al equipo." "we_sent_invite_link_description": "Pide a todos los invitados que revisen su bandeja de entrada. Tienen que hacer clic en el enlace para unirse al equipo."
}, },
"team_environment": { "team_environment": {
"deleted": "Entorno eliminado", "deleted": "Entorno eliminado",

View File

@@ -1,15 +1,15 @@
{ {
"action": { "action": {
"add": "Add", "add": "Ajouter",
"autoscroll": "Autoscroll", "autoscroll": "Auto-scroll",
"cancel": "Annuler", "cancel": "Annuler",
"choose_file": "Choisir un fichier", "choose_file": "Choisir un fichier",
"clear": "Effacer", "clear": "Effacer",
"clear_all": "Tout effacer", "clear_all": "Tout effacer",
"clear_history": "Clear all History", "clear_history": "Effacer tout l'historique",
"close": "Close", "close": "Fermer",
"connect": "Connecter", "connect": "Connecter",
"connecting": "Connecting", "connecting": "Connexion",
"copy": "Copier", "copy": "Copier",
"create": "Create", "create": "Create",
"delete": "Supprimer", "delete": "Supprimer",
@@ -22,8 +22,8 @@
"edit": "Éditer", "edit": "Éditer",
"filter": "Filter", "filter": "Filter",
"go_back": "Retour", "go_back": "Retour",
"go_forward": "Go forward", "go_forward": "Avancer",
"group_by": "Group by", "group_by": "Grouper par",
"label": "Étiqueter", "label": "Étiqueter",
"learn_more": "En savoir plus", "learn_more": "En savoir plus",
"less": "Moins", "less": "Moins",
@@ -35,16 +35,16 @@
"prettify": "Formater", "prettify": "Formater",
"properties": "Properties", "properties": "Properties",
"remove": "Supprimer", "remove": "Supprimer",
"rename": "Rename", "rename": "Renommer",
"restore": "Restaurer", "restore": "Restaurer",
"save": "Sauvegarder", "save": "Sauvegarder",
"scroll_to_bottom": "Scroll to bottom", "scroll_to_bottom": "Défiler vers le bas",
"scroll_to_top": "Scroll to top", "scroll_to_top": "Défiler vers le haut",
"search": "Chercher", "search": "Chercher",
"send": "Envoyer", "send": "Envoyer",
"share": "Share", "share": "Share",
"start": "Démarrer", "start": "Démarrer",
"starting": "Starting", "starting": "Démarrage",
"stop": "Arrêter", "stop": "Arrêter",
"to_close": "pour fermer", "to_close": "pour fermer",
"to_navigate": "pour naviguer", "to_navigate": "pour naviguer",
@@ -86,8 +86,8 @@
"search": "Chercher", "search": "Chercher",
"share": "Partager", "share": "Partager",
"shortcuts": "Raccourcis", "shortcuts": "Raccourcis",
"social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", "social_description": "Suivez-nous sur les médias sociaux pour rester informé des dernières nouvelles, mises à jour et communiqués.",
"social_links": "Social links", "social_links": "Liens sociaux",
"spotlight": "Projecteur", "spotlight": "Projecteur",
"status": "Statut", "status": "Statut",
"status_description": "Vérifier l'état du site web", "status_description": "Vérifier l'état du site web",
@@ -95,7 +95,7 @@
"twitter": "Twitter", "twitter": "Twitter",
"type_a_command_search": "Tapez une commande ou recherchez…", "type_a_command_search": "Tapez une commande ou recherchez…",
"we_use_cookies": "Nous utilisons des cookies", "we_use_cookies": "Nous utilisons des cookies",
"whats_new": "Quoi de neuf?", "whats_new": "Quoi de neuf ?",
"wiki": "Wiki" "wiki": "Wiki"
}, },
"auth": { "auth": {
@@ -119,39 +119,38 @@
}, },
"authorization": { "authorization": {
"generate_token": "Générer un jeton", "generate_token": "Générer un jeton",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Les en-têtes d'autorisation sont envoyés en tant que partie de la charge utile de connection_init.",
"include_in_url": "Inclure dans l'URL", "include_in_url": "Inclure dans l'URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Apprendre comment", "learn": "Apprendre comment",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Mot de passe", "password": "Mot de passe",
"save_to_inherit": "Please save this request in any collection to inherit the authorization", "save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Jeton", "token": "Jeton",
"type": "Type d'autorisation", "type": "Type d'autorisation",
"username": "Nom d'utilisateur" "username": "Nom d'utilisateur",
"oauth": {
"something_went_wrong_on_token_generation": "Un problème s'est produit lors de la génération des jetons",
"redirect_auth_server_returned_error": "Le serveur d'authentification a renvoyé un état d'erreur",
"redirect_no_auth_code": "Pas de code d'autorisation dans la redirection",
"redirect_invalid_state": "Valeur d'état non valide présente dans la redirection",
"redirect_no_token_endpoint": "Aucun point de terminaison de jeton n'est défini",
"redirect_no_client_id": "Pas d'ID client défini",
"redirect_no_client_secret": "Pas de secret client défini",
"redirect_no_code_verifier": "Pas de vérificateur de code défini",
"redirect_auth_token_request_failed": "La demande d'obtention du jeton d'authentification a échoué",
"redirect_auth_token_request_invalid_response": "Réponse invalide du point de terminaison Token lors de la demande d'un jeton d'authentification",
"something_went_wrong_on_oauth_redirect": "Quelque chose s'est mal passé lors de la redirection OAuth"
}
}, },
"collection": { "collection": {
"created": "Collection créée", "created": "Collection créée",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Impossible de réorganiser une collection dont le parent est différent",
"edit": "Modifier la collection", "edit": "Modifier la collection",
"import_or_create": "Import or create a collection", "import_or_create": "Importer ou créer une collection",
"invalid_name": "Veuillez fournir un nom valide pour la collection", "invalid_name": "Veuillez fournir un nom valide pour la collection",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection déjà présente dans la racine",
"moved": "Moved Successfully", "moved": "Déplacement réussi",
"my_collections": "Mes collections", "my_collections": "Mes collections",
"name": "Ma nouvelle collection", "name": "Ma nouvelle collection",
"name_length_insufficient": "Le nom de la collection doit comporter au moins 3 caractères", "name_length_insufficient": "Le nom de la collection doit comporter au moins 3 caractères",
@@ -162,16 +161,16 @@
"renamed": "Collection renommée", "renamed": "Collection renommée",
"request_in_use": "Demande en cours d'utilisation", "request_in_use": "Demande en cours d'utilisation",
"save_as": "Enregistrer sous", "save_as": "Enregistrer sous",
"save_to_collection": "Save to Collection", "save_to_collection": "Enregistrer dans la collection",
"select": "Sélectionnez une collection", "select": "Sélectionnez une collection",
"select_location": "Sélectionnez l'emplacement", "select_location": "Sélectionnez l'emplacement",
"select_team": "Sélectionnez une équipe", "select_team": "Sélectionnez une équipe",
"team_collections": "Collections de l'équipe" "team_collections": "Collections de l'équipe"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "Are you sure you want to close this tab?", "close_unsaved_tab": "Êtes-vous sûr de vouloir fermer cet onglet ?",
"close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", "close_unsaved_tabs": "Êtes-vous sûr de vouloir fermer tous les onglets ? {count} onglets non enregistrés seront perdus",
"exit_team": "Are you sure you want to leave this team?", "exit_team": "Êtes-vous sûr de vouloir quitter cette équipe ?",
"logout": "Êtes-vous sûr de vouloir vous déconnecter?", "logout": "Êtes-vous sûr de vouloir vous déconnecter?",
"remove_collection": "Voulez-vous vraiment supprimer définitivement cette collection ?", "remove_collection": "Voulez-vous vraiment supprimer définitivement cette collection ?",
"remove_environment": "Voulez-vous vraiment supprimer définitivement cet environnement ?", "remove_environment": "Voulez-vous vraiment supprimer définitivement cet environnement ?",
@@ -181,31 +180,31 @@
"remove_shared_request": "Are you sure you want to permanently delete this shared request?", "remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Voulez-vous vraiment supprimer cette équipe ?", "remove_team": "Voulez-vous vraiment supprimer cette équipe ?",
"remove_telemetry": "Êtes-vous sûr de vouloir désactiver la télémétrie ?", "remove_telemetry": "Êtes-vous sûr de vouloir désactiver la télémétrie ?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Êtes-vous sûr de vouloir rejeter la demande en cours ? Les modifications non enregistrées seront perdues.",
"save_unsaved_tab": "Do you want to save changes made in this tab?", "save_unsaved_tab": "Souhaitez-vous enregistrer les modifications apportées dans cet onglet ?",
"sync": "Voulez-vous vraiment synchroniser cet espace de travail ?" "sync": "Voulez-vous vraiment synchroniser cet espace de travail ?"
}, },
"context_menu": { "context_menu": {
"add_parameters": "Add to parameters", "add_parameters": "Ajouter aux paramètres",
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Ouvrir la demande dans un nouvel onglet",
"set_environment_variable": "Set as variable" "set_environment_variable": "Définir comme variable"
}, },
"cookies": { "cookies": {
"modal": { "modal": {
"cookie_expires": "Expires", "new_domain_name": "Nouveau nom de domaine",
"cookie_name": "Name", "set": "Définir un cookie",
"cookie_path": "Path", "cookie_string": "Chaîne de caractères de cookie",
"cookie_string": "Cookie string", "enter_cookie_string": "Saisir la chaîne de caractères du cookie",
"cookie_value": "Value", "cookie_name": "Nom",
"empty_domain": "Domain is empty", "cookie_value": "Valeur",
"empty_domains": "Domain list is empty", "cookie_path": "Chemin d'accès",
"enter_cookie_string": "Enter cookie string", "cookie_expires": "Expiration",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", "managed_tab": "Gestion",
"managed_tab": "Managed", "raw_tab": "Brut",
"new_domain_name": "New domain name", "interceptor_no_support": "L'intercepteur que vous avez sélectionné ne prend pas en charge les cookies. Sélectionnez un autre intercepteur et réessayez.",
"no_cookies_in_domain": "No cookies set for this domain", "empty_domains": "La liste des domaines est vide",
"raw_tab": "Raw", "empty_domain": "Le domaine est vide",
"set": "Set a cookie" "no_cookies_in_domain": "Aucun cookie n'est défini pour ce domaine"
} }
}, },
"count": { "count": {
@@ -238,7 +237,7 @@
"profile": "Connectez-vous pour voir votre profil", "profile": "Connectez-vous pour voir votre profil",
"protocols": "Les protocoles sont vides", "protocols": "Les protocoles sont vides",
"schema": "Se connecter à un point de terminaison GraphQL", "schema": "Se connecter à un point de terminaison GraphQL",
"shared_requests": "Shared requests are empty", "shared_requests": "Il n'y a pas de requêtes partagées",
"shared_requests_logout": "Login to view your shared requests or create a new one", "shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Nom de l'équipe vide", "team_name": "Nom de l'équipe vide",
@@ -252,15 +251,15 @@
"create_new": "Créer un nouvel environnement", "create_new": "Créer un nouvel environnement",
"created": "Environnement créé", "created": "Environnement créé",
"deleted": "Environnement supprimé", "deleted": "Environnement supprimé",
"duplicated": "Environment duplicated", "duplicated": "Environnement dupliqué",
"edit": "Modifier l'environnement", "edit": "Modifier l'environnement",
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Variables globales",
"import_or_create": "Import or create a environment", "import_or_create": "Importer ou créer un environnement",
"invalid_name": "Veuillez fournir un nom valide pour l'environnement", "invalid_name": "Veuillez fournir un nom valide pour l'environnement",
"list": "Environment variables", "list": "Variables d'environnement",
"my_environments": "My Environments", "my_environments": "Mes environnements",
"name": "Name", "name": "Name",
"nested_overflow": "les variables d'environnement imbriquées sont limitées à 10 niveaux", "nested_overflow": "les variables d'environnement imbriquées sont limitées à 10 niveaux",
"new": "Nouvel environnement", "new": "Nouvel environnement",
@@ -284,11 +283,11 @@
"authproviders_load_error": "Unable to load auth providers", "authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.", "browser_support_sse": "Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.",
"check_console_details": "Consultez le journal de la console pour plus de détails.", "check_console_details": "Consultez le journal de la console pour plus de détails.",
"check_how_to_add_origin": "Check how you can add an origin", "check_how_to_add_origin": "Vérifiez comment vous pouvez ajouter une origine",
"curl_invalid_format": "cURL n'est pas formaté correctement", "curl_invalid_format": "cURL n'est pas formaté correctement",
"danger_zone": "Danger zone", "danger_zone": "Zone de danger",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Votre compte est actuellement propriétaire de ces équipes :",
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", "delete_account_description": "Vous devez vous retirer, transférer la propriété ou supprimer ces équipes avant de pouvoir supprimer votre compte.",
"empty_req_name": "Nom de la requête vide", "empty_req_name": "Nom de la requête vide",
"f12_details": "(F12 pour les détails)", "f12_details": "(F12 pour les détails)",
"gql_prettify_invalid_query": "Impossible de formater une requête non valide, résolvez les erreurs de syntaxe de la requête et réessayer", "gql_prettify_invalid_query": "Impossible de formater une requête non valide, résolvez les erreurs de syntaxe de la requête et réessayer",
@@ -300,13 +299,13 @@
"json_prettify_invalid_body": "Impossible de formater un corps non valide, résolvez les erreurs de syntaxe json et réessayez", "json_prettify_invalid_body": "Impossible de formater un corps non valide, résolvez les erreurs de syntaxe json et réessayez",
"network_error": "Il semble y avoir une erreur de réseau. Veuillez réessayer.", "network_error": "Il semble y avoir une erreur de réseau. Veuillez réessayer.",
"network_fail": "Impossible d'envoyer la requête", "network_fail": "Impossible d'envoyer la requête",
"no_collections_to_export": "No collections to export. Please create a collection to get started.", "no_collections_to_export": "Aucune collection à exporter. Veuillez créer une collection pour commencer.",
"no_duration": "Pas de durée", "no_duration": "Pas de durée",
"no_environments_to_export": "No environments to export. Please create an environment to get started.", "no_environments_to_export": "Aucun environnement à exporter. Veuillez créer un environnement pour commencer.",
"no_results_found": "Aucune correspondance trouvée", "no_results_found": "Aucune correspondance trouvée",
"page_not_found": "Cette page n'a pas pu être trouvée", "page_not_found": "Cette page n'a pas pu être trouvée",
"please_install_extension": "Please install the extension and add origin to the extension.", "please_install_extension": "Veuillez installer l'extension et ajouter l'origine à l'extension.",
"proxy_error": "Proxy error", "proxy_error": "Erreur de proxy",
"script_fail": "Impossible d'exécuter le script de pré-requête", "script_fail": "Impossible d'exécuter le script de pré-requête",
"something_went_wrong": "Quelque chose s'est mal passé", "something_went_wrong": "Quelque chose s'est mal passé",
"test_script_fail": "Impossible d'exécuter le script post-requête" "test_script_fail": "Impossible d'exécuter le script post-requête"
@@ -320,9 +319,9 @@
"title": "Exportation" "title": "Exportation"
}, },
"filter": { "filter": {
"all": "All", "all": "Tout",
"none": "None", "none": "Aucun",
"starred": "Starred" "starred": "Étoilé"
}, },
"folder": { "folder": {
"created": "Dossier créé", "created": "Dossier créé",
@@ -333,19 +332,19 @@
"renamed": "Dossier renommé" "renamed": "Dossier renommé"
}, },
"graphql": { "graphql": {
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", "connection_switch_confirm": "Voulez-vous vous connecter avec le dernier point de terminaison GraphQL ?",
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", "connection_switch_new_url": "Le passage à un autre onglet vous déconnectera de la connexion GraphQL active. La nouvelle URL de connexion est",
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", "connection_switch_url": "Vous êtes connecté à un point de terminaison GraphQL dont l'URL de connexion est",
"mutations": "Mutations", "mutations": "Mutations",
"schema": "Schéma", "schema": "Schéma",
"subscriptions": "Abonnements", "subscriptions": "Abonnements",
"switch_connection": "Switch connection" "switch_connection": "Changer de connexion"
}, },
"graphql_collections": { "graphql_collections": {
"title": "GraphQL Collections" "title": "GraphQL Collections"
}, },
"group": { "group": {
"time": "Time", "time": "Temps",
"url": "URL" "url": "URL"
}, },
"header": { "header": {
@@ -376,7 +375,7 @@
"import": { "import": {
"collections": "Importer des collections", "collections": "Importer des collections",
"curl": "Importer en cURL", "curl": "Importer en cURL",
"environments_from_gist": "Import From Gist", "environments_from_gist": "Importer depuis Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist", "environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Échec de l'importation", "failed": "Échec de l'importation",
"from_file": "Import from File", "from_file": "Import from File",
@@ -408,27 +407,27 @@
"title": "Importer" "title": "Importer"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Inspecter les erreurs possibles",
"environment": { "environment": {
"add_environment": "Add to Environment", "add_environment": "Ajouter à l'environnement",
"not_found": "Environment variable “{environment} not found." "not_found": "La variable d'environnement “{environment} n'a pas été trouvée."
}, },
"header": { "header": {
"cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." "cookie": "Le navigateur ne permet pas à Hoppscotch de définir l'en-tête Cookie. Pendant que nous travaillons sur l'application de bureau Hoppscotch (bientôt disponible), veuillez utiliser l'en-tête d'autorisation à la place."
}, },
"response": { "response": {
"401_error": "Please check your authentication credentials.", "401_error": "Veuillez vérifier vos informations d'authentification.",
"404_error": "Please check your request URL and method type.", "404_error": "Veuillez vérifier l'URL de votre demande et le type de méthode.",
"cors_error": "Please check your Cross-Origin Resource Sharing configuration.", "cors_error": "Veuillez vérifier la configuration du partage des ressources entre les origines.",
"default_error": "Please check your request.", "default_error": "Veuillez vérifier votre demande.",
"network_error": "Please check your network connection." "network_error": "Veuillez vérifier votre connexion réseau."
}, },
"title": "Inspector", "title": "Inspecteur",
"url": { "url": {
"extension_not_installed": "Extension not installed.", "extension_not_installed": "L'extension n'est pas installée.",
"extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", "extension_unknown_origin": "Assurez-vous d'avoir ajouté l'origine du point de terminaison de l'API à la liste des extensions du navigateur Hoppscotch.",
"extention_enable_action": "Enable Browser Extension", "extention_enable_action": "Activer l'extension du navigateur",
"extention_not_enabled": "Extension not enabled." "extention_not_enabled": "L'extension n'est pas activée."
} }
}, },
"layout": { "layout": {
@@ -439,25 +438,25 @@
"row": "Disposition horizontale" "row": "Disposition horizontale"
}, },
"modal": { "modal": {
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "Vous avez des modifications non enregistrées",
"collections": "Collections", "collections": "Collections",
"confirm": "Confirmer", "confirm": "Confirmer",
"customize_request": "Customize Request", "customize_request": "Customize Request",
"edit_request": "Modifier la requête", "edit_request": "Modifier la requête",
"import_export": "Importer / Exporter", "import_export": "Importer / Exporter",
"share_request": "Share Request" "share_request": "Partager une requête"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "Vous êtes déjà abonné à ce sujet.",
"clean_session": "Clean Session", "clean_session": "Effacer la Session",
"clear_input": "Clear input", "clear_input": "Effacer la saisie",
"clear_input_on_send": "Clear input on send", "clear_input_on_send": "Effacer la saisie lors de l'envoi",
"client_id": "Client ID", "client_id": "Client ID",
"color": "Pick a color", "color": "Choisir la couleur",
"communication": "Communication", "communication": "Communication",
"connection_config": "Connection Config", "connection_config": "Connection Config",
"connection_not_authorized": "This MQTT connection does not use any authentication.", "connection_not_authorized": "Cette connexion MQTT n'utilise pas d'authentification.",
"invalid_topic": "Please provide a topic for the subscription", "invalid_topic": "Veuillez fournir un sujet pour l'abonnement",
"keep_alive": "Keep Alive", "keep_alive": "Keep Alive",
"log": "Infos", "log": "Infos",
"lw_message": "Last-Will Message", "lw_message": "Last-Will Message",
@@ -466,7 +465,7 @@
"lw_topic": "Last-Will Topic", "lw_topic": "Last-Will Topic",
"message": "Message", "message": "Message",
"new": "New Subscription", "new": "New Subscription",
"not_connected": "Please start a MQTT connection first.", "not_connected": "Veuillez d'abord établir une connexion MQTT.",
"publish": "Publier", "publish": "Publier",
"qos": "QoS", "qos": "QoS",
"ssl": "SSL", "ssl": "SSL",
@@ -480,7 +479,7 @@
"navigation": { "navigation": {
"doc": "Documents", "doc": "Documents",
"graphql": "GraphQL", "graphql": "GraphQL",
"profile": "Profile", "profile": "Profil",
"realtime": "Temps réel", "realtime": "Temps réel",
"rest": "REST", "rest": "REST",
"settings": "Paramètres" "settings": "Paramètres"
@@ -493,7 +492,7 @@
}, },
"profile": { "profile": {
"app_settings": "Réglages de l'application", "app_settings": "Réglages de l'application",
"default_hopp_displayname": "Unnamed User", "default_hopp_displayname": "Utilisateur anonyme",
"editor": "Éditeur", "editor": "Éditeur",
"editor_description": "Les éditeurs peuvent ajouter, modifier et supprimer des demandes.", "editor_description": "Les éditeurs peuvent ajouter, modifier et supprimer des demandes.",
"email_verification_mail": "Un e-mail de vérification a été envoyé à votre adresse e-mail. Veuillez cliquer sur le lien pour vérifier votre adresse électronique.", "email_verification_mail": "Un e-mail de vérification a été envoyé à votre adresse e-mail. Veuillez cliquer sur le lien pour vérifier votre adresse électronique.",
@@ -526,17 +525,17 @@
"enter_curl": "Entrer cURL", "enter_curl": "Entrer cURL",
"generate_code": "Générer le code", "generate_code": "Générer le code",
"generated_code": "Code généré", "generated_code": "Code généré",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab", "go_to_body_tab": "Go to Body tab",
"go_to_authorization_tab": "Aller à l'autorisation",
"header_list": "Liste des en-têtes", "header_list": "Liste des en-têtes",
"invalid_name": "Veuillez fournir un nom pour la requête", "invalid_name": "Veuillez fournir un nom pour la requête",
"method": "Méthode", "method": "Méthode",
"moved": "Request moved", "moved": "Request moved",
"name": "Nom de la requête", "name": "Nom de la requête",
"new": "Nouvelle requête", "new": "Nouvelle requête",
"order_changed": "Request Order Updated", "order_changed": "Demande de commande Mise à jour",
"override": "Remplacer", "override": "Remplacer",
"override_help": "Set <xmp>Content-Type</xmp> in Headers", "override_help": "Définir <xmp>Content-Type</xmp> dans les en-têtes",
"overriden": "Remplacé", "overriden": "Remplacé",
"parameter_list": "Paramètres de requête", "parameter_list": "Paramètres de requête",
"parameters": "Paramètres", "parameters": "Paramètres",
@@ -544,7 +543,7 @@
"payload": "Charge utile", "payload": "Charge utile",
"query": "Requête", "query": "Requête",
"raw_body": "Corps de requête brut", "raw_body": "Corps de requête brut",
"rename": "Rename Request", "rename": "Demande de renommage",
"renamed": "Requête renommée", "renamed": "Requête renommée",
"run": "Lancer", "run": "Lancer",
"save": "Sauvegarder", "save": "Sauvegarder",
@@ -564,7 +563,7 @@
"response": { "response": {
"audio": "Audio", "audio": "Audio",
"body": "Corps de réponse", "body": "Corps de réponse",
"filter_response_body": "Filter JSON response body (uses JSONPath syntax)", "filter_response_body": "Filtrer le corps de la réponse JSON (utilise la syntaxe JSONPath)",
"headers": "En-têtes", "headers": "En-têtes",
"html": "HTML", "html": "HTML",
"image": "Image", "image": "Image",
@@ -576,14 +575,14 @@
"status": "Statut", "status": "Statut",
"time": "Temps", "time": "Temps",
"title": "Réponse", "title": "Réponse",
"video": "Video", "video": "Vidéo",
"waiting_for_connection": "En attente de connexion", "waiting_for_connection": "En attente de connexion",
"xml": "XML" "xml": "XML"
}, },
"settings": { "settings": {
"accent_color": "Couleur d'accent", "accent_color": "Couleur d'accent",
"account": "Compte", "account": "Compte",
"account_deleted": "Your account has been deleted", "account_deleted": "Votre compte a été supprimé",
"account_description": "Personnalisez les paramètres de votre compte.", "account_description": "Personnalisez les paramètres de votre compte.",
"account_email_description": "Votre adresse e-mail principale.", "account_email_description": "Votre adresse e-mail principale.",
"account_name_description": "Ceci est votre nom d'affichage.", "account_name_description": "Ceci est votre nom d'affichage.",
@@ -592,8 +591,8 @@
"black_mode": "Noir", "black_mode": "Noir",
"choose_language": "Choisissez la langue", "choose_language": "Choisissez la langue",
"dark_mode": "Sombre", "dark_mode": "Sombre",
"delete_account": "Delete account", "delete_account": "Supprimer le compte",
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.", "delete_account_description": "Lorsque vous supprimez votre compte, toutes vos données sont définitivement effacées. Cette action ne peut être annulée.",
"expand_navigation": "Expand navigation", "expand_navigation": "Expand navigation",
"experiments": "Expériences", "experiments": "Expériences",
"experiments_notice": "Il s'agit d'une collection d'expériences sur lesquelles nous travaillons et qui pourraient s'avérer utiles, amusantes, les deux ou aucune. Ils ne sont pas définitifs et peuvent ne pas être stables, donc si quelque chose de trop étrange se produit, ne paniquez pas. Il suffit d'éteindre le truc. Blague à part,", "experiments_notice": "Il s'agit d'une collection d'expériences sur lesquelles nous travaillons et qui pourraient s'avérer utiles, amusantes, les deux ou aucune. Ils ne sont pas définitifs et peuvent ne pas être stables, donc si quelque chose de trop étrange se produit, ne paniquez pas. Il suffit d'éteindre le truc. Blague à part,",
@@ -601,7 +600,7 @@
"extension_version": "Version d'extension", "extension_version": "Version d'extension",
"extensions": "Extensions", "extensions": "Extensions",
"extensions_use_toggle": "Utilisez l'extension de navigateur pour envoyer des requêtes (le cas échéant)", "extensions_use_toggle": "Utilisez l'extension de navigateur pour envoyer des requêtes (le cas échéant)",
"follow": "Follow Us", "follow": "Suivez-nous",
"interceptor": "Intercepteur", "interceptor": "Intercepteur",
"interceptor_description": "Middleware entre l'application et les API.", "interceptor_description": "Middleware entre l'application et les API.",
"language": "Langue", "language": "Langue",
@@ -631,7 +630,7 @@
"theme_description": "Personnalisez le thème de votre application.", "theme_description": "Personnalisez le thème de votre application.",
"use_experimental_url_bar": "Utiliser la barre d'URL expérimentale avec mise en évidence de l'environnement", "use_experimental_url_bar": "Utiliser la barre d'URL expérimentale avec mise en évidence de l'environnement",
"user": "Utilisateur", "user": "Utilisateur",
"verified_email": "Verified email", "verified_email": "E-mail vérifié",
"verify_email": "Vérifier l'email" "verify_email": "Vérifier l'email"
}, },
"shared_requests": { "shared_requests": {
@@ -684,20 +683,20 @@
"title": "Navigation" "title": "Navigation"
}, },
"others": { "others": {
"prettify": "Prettify Editor's Content", "prettify": "Améliorer le contenu de l'éditeur",
"title": "Others" "title": "Autres"
}, },
"request": { "request": {
"delete_method": "Sélectionnez la méthode DELETE", "delete_method": "Sélectionnez la méthode DELETE",
"get_method": "Sélectionnez la méthode GET", "get_method": "Sélectionnez la méthode GET",
"head_method": "Sélectionnez la méthode HEAD", "head_method": "Sélectionnez la méthode HEAD",
"import_curl": "Import cURL", "import_curl": "Importer cURL",
"method": "Méthode", "method": "Méthode",
"next_method": "Sélectionnez la méthode suivante", "next_method": "Sélectionnez la méthode suivante",
"post_method": "Sélectionnez la méthode POST", "post_method": "Sélectionnez la méthode POST",
"previous_method": "Sélectionnez la méthode précédente", "previous_method": "Sélectionnez la méthode précédente",
"put_method": "Sélectionnez la méthode PUT", "put_method": "Sélectionnez la méthode PUT",
"rename": "Rename Request", "rename": "Demande de renommage",
"reset_request": "Réinitialiser la requête", "reset_request": "Réinitialiser la requête",
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Enregistrer dans les collections", "save_to_collections": "Enregistrer dans les collections",
@@ -728,89 +727,89 @@
}, },
"socketio": { "socketio": {
"communication": "Communication", "communication": "Communication",
"connection_not_authorized": "This SocketIO connection does not use any authentication.", "connection_not_authorized": "Cette connexion SocketIO n'utilise pas d'authentification.",
"event_name": "Nom de l'événement", "event_name": "Nom de l'événement",
"events": "Événements", "events": "Événements",
"log": "Infos", "log": "Infos",
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "Change Language", "change_language": "Changer de langue",
"environments": { "environments": {
"delete": "Delete current environment", "delete": "Supprimer l'environnement actuel",
"duplicate": "Duplicate current environment", "duplicate": "Dupliquer l'environnement actuel",
"duplicate_global": "Duplicate global environment", "duplicate_global": "Duplication de l'environnement global",
"edit": "Edit current environment", "edit": "Modifier l'environnement actuel",
"edit_global": "Edit global environment", "edit_global": "Modifier l'environnement mondial",
"new": "Create new environment", "new": "Créer un nouvel environnement",
"new_variable": "Create a new environment variable", "new_variable": "Créer une nouvelle variable d'environnement",
"title": "Environments" "title": "Environments"
}, },
"general": { "general": {
"chat": "Chat with support", "chat": "Chat avec le support",
"help_menu": "Help and support", "help_menu": "Aide et assistance",
"open_docs": "Read Documentation", "open_docs": "Lire la documentation",
"open_github": "Open GitHub repository", "open_github": "Ouvrir le dépôt GitHub",
"open_keybindings": "Keyboard shortcuts", "open_keybindings": "Raccourcis clavier",
"social": "Social", "social": "Social",
"title": "General" "title": "Général"
}, },
"graphql": { "graphql": {
"connect": "Connect to server", "connect": "Connexion au serveur",
"disconnect": "Disconnect from server" "disconnect": "Déconnexion du serveur"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "Invite your friends to Hoppscotch", "invite": "Invitez vos amis à Hoppscotch",
"title": "Miscellaneous" "title": "Divers"
}, },
"request": { "request": {
"save_as_new": "Save as new request", "save_as_new": "Sauvegarder comme nouvelle demande",
"select_method": "Select method", "select_method": "Sélectionner la méthode",
"switch_to": "Switch to", "switch_to": "Basculer vers",
"tab_authorization": "Authorization tab", "tab_authorization": "Onglet Autorisation",
"tab_body": "Body tab", "tab_body": "Onglet du corps",
"tab_headers": "Headers tab", "tab_headers": "Onglet En-têtes",
"tab_parameters": "Parameters tab", "tab_parameters": "Onglet Paramètres",
"tab_pre_request_script": "Pre-request script tab", "tab_pre_request_script": "Onglet script de pré-demande",
"tab_query": "Query tab", "tab_query": "Onglet Requête",
"tab_tests": "Tests tab", "tab_tests": "Onglet Tests",
"tab_variables": "Variables tab" "tab_variables": "Onglet Variables"
}, },
"response": { "response": {
"copy": "Copy response", "copy": "Copier la réponse",
"download": "Download response as file", "download": "Télécharger la réponse sous forme de fichier",
"title": "Response" "title": "Réponse"
}, },
"section": { "section": {
"interceptor": "Interceptor", "interceptor": "Intercepteur",
"interface": "Interface", "interface": "Interface",
"theme": "Theme", "theme": "Thème",
"user": "User" "user": "Utilisateur"
}, },
"settings": { "settings": {
"change_interceptor": "Change Interceptor", "change_interceptor": "Changer d'intercepteur",
"change_language": "Change Language", "change_language": "Changer de langue",
"theme": { "theme": {
"black": "Black", "black": "Noir",
"dark": "Dark", "dark": "Sombre",
"light": "Light", "light": "Clair",
"system": "System preference" "system": "Préférence du système"
} }
}, },
"tab": { "tab": {
"close_current": "Close current tab", "close_current": "Fermer l'onglet actuel",
"close_others": "Close all other tabs", "close_others": "Fermer tous les autres onglets",
"duplicate": "Duplicate current tab", "duplicate": "Dupliquer l'onglet actuel",
"new_tab": "Open a new tab", "new_tab": "Ouvrir un nouvel onglet",
"title": "Tabs" "title": "Onglets"
}, },
"workspace": { "workspace": {
"delete": "Delete current team", "delete": "Supprimer l'équipe actuelle",
"edit": "Edit current team", "edit": "Modifier l'équipe actuelle",
"invite": "Invite people to team", "invite": "Inviter les gens à rejoindre l'équipe",
"new": "Create new team", "new": "Créer une nouvelle équipe",
"switch_to_personal": "Switch to your personal workspace", "switch_to_personal": "Passez à votre espace de travail personnel",
"title": "Teams" "title": "Les équipes"
} }
}, },
"sse": { "sse": {
@@ -836,12 +835,12 @@
"disconnected": "Déconnecté", "disconnected": "Déconnecté",
"disconnected_from": "Déconnecté de {name}", "disconnected_from": "Déconnecté de {name}",
"docs_generated": "Documentation générée", "docs_generated": "Documentation générée",
"download_failed": "Download failed",
"download_started": "Téléchargement commencé", "download_started": "Téléchargement commencé",
"download_failed": "Téléchargement échoué",
"enabled": "Active", "enabled": "Active",
"file_imported": "Fichier importé", "file_imported": "Fichier importé",
"finished_in": "Terminé en {duration} ms", "finished_in": "Terminé en {duration} ms",
"hide": "Hide", "hide": "Cacher",
"history_deleted": "Historique supprimé", "history_deleted": "Historique supprimé",
"linewrap": "Retour à la ligne", "linewrap": "Retour à la ligne",
"loading": "Chargement...", "loading": "Chargement...",
@@ -852,7 +851,7 @@
"published_error": "Quelque chose s'est mal passé lors de la publication du message : {topic} dans le sujet : {message}", "published_error": "Quelque chose s'est mal passé lors de la publication du message : {topic} dans le sujet : {message}",
"published_message": "Message publié : {message} au sujet : {topic}", "published_message": "Message publié : {message} au sujet : {topic}",
"reconnection_error": "Échec de la reconnexion", "reconnection_error": "Échec de la reconnexion",
"show": "Show", "show": "Afficher",
"subscribed_failed": "Échec de l'inscription au sujet : {topic}", "subscribed_failed": "Échec de l'inscription au sujet : {topic}",
"subscribed_success": "Inscription réussie au sujet : {topic}", "subscribed_success": "Inscription réussie au sujet : {topic}",
"unsubscribed_failed": "Échec de la désinscription du sujet : {topic}", "unsubscribed_failed": "Échec de la désinscription du sujet : {topic}",
@@ -861,7 +860,7 @@
}, },
"support": { "support": {
"changelog": "En savoir plus sur les dernières versions", "changelog": "En savoir plus sur les dernières versions",
"chat": "Des questions? Discutez avec nous!", "chat": "Des questions ? Discutez avec nous!",
"community": "Posez des questions et aidez les autres", "community": "Posez des questions et aidez les autres",
"documentation": "En savoir plus sur Hoppscotch", "documentation": "En savoir plus sur Hoppscotch",
"forum": "Posez des questions et obtenez des réponses", "forum": "Posez des questions et obtenez des réponses",
@@ -874,21 +873,21 @@
"tab": { "tab": {
"authorization": "Autorisation", "authorization": "Autorisation",
"body": "Corps", "body": "Corps",
"close": "Close Tab", "close": "Fermer l'onglet",
"close_others": "Close other Tabs", "close_others": "Fermer les autres onglets",
"collections": "Collections", "collections": "Collections",
"documentation": "Documentation", "documentation": "Documentation",
"duplicate": "Duplicate Tab", "duplicate": "Dupliquer l'onglet",
"environments": "Environments", "environments": "Environments",
"headers": "En-têtes", "headers": "En-têtes",
"history": "Histoire", "history": "Historique",
"mqtt": "MQTT", "mqtt": "MQTT",
"parameters": "Paramètres", "parameters": "Paramètres",
"pre_request_script": "Script de pré-requête", "pre_request_script": "Script de pré-requête",
"queries": "Requêtes", "queries": "Requêtes",
"query": "Requête", "query": "Requête",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests", "shared_requests": "Requêtes partagées",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "ESS", "sse": "ESS",
"tests": "Tests", "tests": "Tests",
@@ -905,7 +904,6 @@
"email_do_not_match": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.", "email_do_not_match": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.",
"exit": "Quitter l'équipe", "exit": "Quitter l'équipe",
"exit_disabled": "Seul le propriétaire ne peut pas quitter l'équipe", "exit_disabled": "Seul le propriétaire ne peut pas quitter l'équipe",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Le format de l'e-mail n'est pas valide", "invalid_email_format": "Le format de l'e-mail n'est pas valide",
"invalid_id": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.", "invalid_id": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.",
@@ -941,21 +939,22 @@
"no_request_found": "Request not found.", "no_request_found": "Request not found.",
"not_found": "Équipe non trouvée. Contactez le propriétaire de votre équipe.", "not_found": "Équipe non trouvée. Contactez le propriétaire de votre équipe.",
"not_valid_viewer": "Vous n'êtes pas un visionneur valide. Contactez le propriétaire de votre équipe.", "not_valid_viewer": "Vous n'êtes pas un visionneur valide. Contactez le propriétaire de votre équipe.",
"parent_coll_move": "Cannot move collection to a child collection", "parent_coll_move": "Impossible de déplacer une collection vers une collection enfant",
"success_invites": "Les invitations réussites",
"pending_invites": "Invitations en attente", "pending_invites": "Invitations en attente",
"failed_invites": "Échec des invitations",
"permissions": "Autorisations", "permissions": "Autorisations",
"same_target_destination": "Same target and destination", "same_target_destination": "me destinataire et même cible",
"saved": "Équipe enregistrée", "saved": "Équipe enregistrée",
"select_a_team": "Choisir une équipe", "select_a_team": "Choisir une équipe",
"success_invites": "Success invites",
"title": "Équipes", "title": "Équipes",
"we_sent_invite_link": "Nous avons envoyé un lien d'invitation à tous les invités !", "we_sent_invite_link": "Nous avons envoyé un lien d'invitation à tous les invités !",
"we_sent_invite_link_description": "Demandez à tous les invités de vérifier leur boîte de réception. Cliquez sur le lien pour rejoindre l'équipe." "we_sent_invite_link_description": "Demandez à tous les invités de vérifier leur boîte de réception. Cliquez sur le lien pour rejoindre l'équipe."
}, },
"team_environment": { "team_environment": {
"deleted": "Environment Deleted", "deleted": "Environment supprimé",
"duplicate": "Environment Duplicated", "duplicate": "Environment dupliqué",
"not_found": "Environment not found." "not_found": "Environment non trouvé"
}, },
"test": { "test": {
"failed": "Test échoué", "failed": "Test échoué",
@@ -975,10 +974,10 @@
"url": "URL" "url": "URL"
}, },
"workspace": { "workspace": {
"change": "Change workspace", "change": "Changer d'espace de travail",
"personal": "My Workspace", "personal": "Mon espace de travail",
"team": "Team Workspace", "team": "Espace de travail de l'équipe",
"title": "Workspaces" "title": "Espaces de travail"
}, },
"shortcodes": { "shortcodes": {
"actions": "Actions", "actions": "Actions",

View File

@@ -1,17 +1,17 @@
{ {
"action": { "action": {
"add": "Add", "add": "Hozzáadás",
"autoscroll": "Automatikus görgetés", "autoscroll": "Automatikus görgetés",
"cancel": "Mégse", "cancel": "Mégse",
"choose_file": "Válasszon egy fájlt", "choose_file": "Válasszon egy fájlt",
"clear": "Törlés", "clear": "Törlés",
"clear_all": "Összes törlése", "clear_all": "Összes törlése",
"clear_history": "Clear all History", "clear_history": "Összes előzmény törlése",
"close": "Bezárás", "close": "Bezárás",
"connect": "Kapcsolódás", "connect": "Kapcsolódás",
"connecting": "Kapcsolódás", "connecting": "Kapcsolódás",
"copy": "Másolás", "copy": "Másolás",
"create": "Create", "create": "Létrehozás",
"delete": "Törlés", "delete": "Törlés",
"disconnect": "Leválasztás", "disconnect": "Leválasztás",
"dismiss": "Eltüntetés", "dismiss": "Eltüntetés",
@@ -33,16 +33,16 @@
"open_workspace": "Munkaterület megnyitása", "open_workspace": "Munkaterület megnyitása",
"paste": "Beillesztés", "paste": "Beillesztés",
"prettify": "Csinosítás", "prettify": "Csinosítás",
"properties": "Properties", "properties": "Tulajdonságok",
"remove": "Eltávolítás", "remove": "Eltávolítás",
"rename": "Rename", "rename": "Átnevezés",
"restore": "Visszaállítás", "restore": "Visszaállítás",
"save": "Mentés", "save": "Mentés",
"scroll_to_bottom": "Görgetés az aljára", "scroll_to_bottom": "Görgetés az aljára",
"scroll_to_top": "Görgetés a tetejére", "scroll_to_top": "Görgetés a tetejére",
"search": "Keresés", "search": "Keresés",
"send": "Küldés", "send": "Küldés",
"share": "Share", "share": "Megosztás",
"start": "Indítás", "start": "Indítás",
"starting": "Indítás", "starting": "Indítás",
"stop": "Leállítás", "stop": "Leállítás",
@@ -61,9 +61,9 @@
"app": { "app": {
"chat_with_us": "Csevegjen velünk", "chat_with_us": "Csevegjen velünk",
"contact_us": "Lépjen kapcsolatba velünk", "contact_us": "Lépjen kapcsolatba velünk",
"cookies": "Cookies", "cookies": "Sütik",
"copy": "Másolás", "copy": "Másolás",
"copy_interface_type": "Copy interface type", "copy_interface_type": "Interface típusának másolása",
"copy_user_id": "Felhasználó-hitelesítési token másolása", "copy_user_id": "Felhasználó-hitelesítési token másolása",
"developer_option": "Fejlesztői beállítások", "developer_option": "Fejlesztői beállítások",
"developer_option_description": "Fejlesztői eszközök, amelyek segítenek a Hoppscotch fejlesztésében és karbantartásában.", "developer_option_description": "Fejlesztői eszközök, amelyek segítenek a Hoppscotch fejlesztésében és karbantartásában.",
@@ -79,15 +79,15 @@
"keyboard_shortcuts": "Gyorsbillentyűk", "keyboard_shortcuts": "Gyorsbillentyűk",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Új verzió található. Töltse újra az oldalt a frissítéshez.", "new_version_found": "Új verzió található. Töltse újra az oldalt a frissítéshez.",
"open_in_hoppscotch": "Open in Hoppscotch", "open_in_hoppscotch": "Megnyitás Hoppscotch-ban.",
"options": "Beállítások", "options": "Beállítások",
"proxy_privacy_policy": "Proxy adatvédelmi irányelvei", "proxy_privacy_policy": "Proxy adatvédelmi irányelvei",
"reload": "Újratöltés", "reload": "Újratöltés",
"search": "Keresés", "search": "Keresés",
"share": "Megosztás", "share": "Megosztás",
"shortcuts": "Gyorsbillentyűk", "shortcuts": "Gyorsbillentyűk",
"social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", "social_description": "Kövess minket a közösségi médiában, hogy ne maradj le a hírekről, frissítésekről és új kiadásokról.",
"social_links": "Social links", "social_links": "Közösségi média linkek",
"spotlight": "Reflektorfény", "spotlight": "Reflektorfény",
"status": "Állapot", "status": "Állapot",
"status_description": "A weboldal állapotának ellenőrzése", "status_description": "A weboldal állapotának ellenőrzése",
@@ -119,27 +119,27 @@
}, },
"authorization": { "authorization": {
"generate_token": "Token előállítása", "generate_token": "Token előállítása",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Azonosító fejléc connection_init tartalmaként elküldve",
"include_in_url": "Felvétel az URL-be", "include_in_url": "Felvétel az URL-be",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "inherited_from": "Örökölt a(z) {auth}-tól, a(z) {collection} gyűjteményből ",
"learn": "Tudja meg, hogyan", "learn": "Tudja meg, hogyan",
"oauth": { "oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Az Auth szerver hibás állapottal tért vissza",
"redirect_auth_token_request_failed": "Request to get the auth token failed", "redirect_auth_token_request_failed": "Kérés az auth token lekéréséhez sikertelen",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token", "redirect_auth_token_request_invalid_response": "Érvénytelen válasz a Token Endpoint-tól, az auth token lekérésekpr",
"redirect_invalid_state": "Invalid State value present in the redirect", "redirect_invalid_state": "Érvénytelen állapotérték az átirányításban",
"redirect_no_auth_code": "No Authorization Code present in the redirect", "redirect_no_auth_code": "Nincs azonosítás az átirányításban",
"redirect_no_client_id": "No Client ID defined", "redirect_no_client_id": "Nincs felhasználó azonosító",
"redirect_no_client_secret": "No Client Secret Defined", "redirect_no_client_secret": "Nincs felhasználó jelszó",
"redirect_no_code_verifier": "No Code Verifier Defined", "redirect_no_code_verifier": "Nincs kódellenőrző",
"redirect_no_token_endpoint": "No Token Endpoint Defined", "redirect_no_token_endpoint": "Nincs \"Token Endpoint\"",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_oauth_redirect": "Valami rosszul sikerült az OAuth átirányításakor",
"something_went_wrong_on_token_generation": "Something went wrong on token generation", "something_went_wrong_on_token_generation": "Valami rosszul sikerült a token generálásakor",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" "token_generation_oidc_discovery_failed": "Hiba a token generálásakor: OpenID Connect Discovery hiba"
}, },
"pass_key_by": "Átadta", "pass_key_by": "Átadta",
"password": "Jelszó", "password": "Jelszó",
"save_to_inherit": "Please save this request in any collection to inherit the authorization", "save_to_inherit": "Kérjük, mentse el ezt kérést bármelyik gyűjteménybe, hogy az azonosítás örökölhető lehessen",
"token": "Token", "token": "Token",
"type": "Felhatalmazás típusa", "type": "Felhatalmazás típusa",
"username": "Felhasználónév" "username": "Felhasználónév"
@@ -148,7 +148,7 @@
"created": "Gyűjtemény létrehozva", "created": "Gyűjtemény létrehozva",
"different_parent": "Nem lehet átrendezni a különböző szülővel rendelkező gyűjteményt", "different_parent": "Nem lehet átrendezni a különböző szülővel rendelkező gyűjteményt",
"edit": "Gyűjtemény szerkesztése", "edit": "Gyűjtemény szerkesztése",
"import_or_create": "Import or create a collection", "import_or_create": "Gyűjtemény importálása vagy létrehozása",
"invalid_name": "Adjon nevet a gyűjteménynek", "invalid_name": "Adjon nevet a gyűjteménynek",
"invalid_root_move": "A gyűjtemény már a gyökérben van", "invalid_root_move": "A gyűjtemény már a gyökérben van",
"moved": "Sikeresen áthelyezve", "moved": "Sikeresen áthelyezve",
@@ -157,20 +157,20 @@
"name_length_insufficient": "A gyűjtemény nevének legalább 3 karakter hosszúságúnak kell lennie", "name_length_insufficient": "A gyűjtemény nevének legalább 3 karakter hosszúságúnak kell lennie",
"new": "Új gyűjtemény", "new": "Új gyűjtemény",
"order_changed": "Gyűjtemény sorrendje frissítve", "order_changed": "Gyűjtemény sorrendje frissítve",
"properties": "Collection Properties", "properties": "Gyűjtemény tulajdonságok",
"properties_updated": "Collection Properties Updated", "properties_updated": "Gyűjtemény tulajdonságai frissítve",
"renamed": "Gyűjtemény átnevezve", "renamed": "Gyűjtemény átnevezve",
"request_in_use": "A kérés használatban", "request_in_use": "A kérés használatban",
"save_as": "Mentés másként", "save_as": "Mentés másként",
"save_to_collection": "Save to Collection", "save_to_collection": "Mentés egy gyűjteménybe",
"select": "Gyűjtemény kiválasztása", "select": "Gyűjtemény kiválasztása",
"select_location": "Hely kiválasztása", "select_location": "Hely kiválasztása",
"select_team": "Csapat kiválasztása", "select_team": "Csapat kiválasztása",
"team_collections": "Csapat gyűjteményei" "team_collections": "Csapat gyűjteményei"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "Are you sure you want to close this tab?", "close_unsaved_tab": "Biztos, hogy bezárja ezt a lapot?",
"close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", "close_unsaved_tabs": "Biztos, hogy bezárja az összes lapot? {count} elmentetlen lap el fog veszni.",
"exit_team": "Biztosan el szeretné hagyni ezt a csapatot?", "exit_team": "Biztosan el szeretné hagyni ezt a csapatot?",
"logout": "Biztosan ki szeretne jelentkezni?", "logout": "Biztosan ki szeretne jelentkezni?",
"remove_collection": "Biztosan véglegesen törölni szeretné ezt a gyűjteményt?", "remove_collection": "Biztosan véglegesen törölni szeretné ezt a gyűjteményt?",
@@ -178,7 +178,7 @@
"remove_folder": "Biztosan véglegesen törölni szeretné ezt a mappát?", "remove_folder": "Biztosan véglegesen törölni szeretné ezt a mappát?",
"remove_history": "Biztosan véglegesen törölni szeretné az összes előzményt?", "remove_history": "Biztosan véglegesen törölni szeretné az összes előzményt?",
"remove_request": "Biztosan véglegesen törölni szeretné ezt a kérést?", "remove_request": "Biztosan véglegesen törölni szeretné ezt a kérést?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?", "remove_shared_request": "Biztos, hogy véglegesen törölni szeretné ezt a megosztott kérést?",
"remove_team": "Biztosan törölni szeretné ezt a csapatot?", "remove_team": "Biztosan törölni szeretné ezt a csapatot?",
"remove_telemetry": "Biztosan ki szeretné kapcsolni a telemetriát?", "remove_telemetry": "Biztosan ki szeretné kapcsolni a telemetriát?",
"request_change": "Biztosan el szeretné vetni a jelenlegi kérést? Minden mentetlen változtatás el fog veszni.", "request_change": "Biztosan el szeretné vetni a jelenlegi kérést? Minden mentetlen változtatás el fog veszni.",
@@ -186,26 +186,26 @@
"sync": "Szeretné visszaállítani a munkaterületét a felhőből? Ez el fogja vetni a helyi folyamatát." "sync": "Szeretné visszaállítani a munkaterületét a felhőből? Ez el fogja vetni a helyi folyamatát."
}, },
"context_menu": { "context_menu": {
"add_parameters": "Add to parameters", "add_parameters": "Paraméterek hozzáadása",
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Kérés megnyitása új lapot",
"set_environment_variable": "Set as variable" "set_environment_variable": "Változóként való beállítás"
}, },
"cookies": { "cookies": {
"modal": { "modal": {
"cookie_expires": "Expires", "cookie_expires": "Lejárat",
"cookie_name": "Name", "cookie_name": "Név",
"cookie_path": "Path", "cookie_path": "Útvonal",
"cookie_string": "Cookie string", "cookie_string": "Süti szöveg",
"cookie_value": "Value", "cookie_value": "Érték",
"empty_domain": "Domain is empty", "empty_domain": "Üres domain",
"empty_domains": "Domain list is empty", "empty_domains": "Domain lista üres",
"enter_cookie_string": "Enter cookie string", "enter_cookie_string": "Süti szövegének megadása",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", "interceptor_no_support": "A kiválasztott interceptor nem támogatja a sütiket. Válasszon ki egy másik interceptor-t és próbálja újra.",
"managed_tab": "Managed", "managed_tab": "Menedzselt",
"new_domain_name": "New domain name", "new_domain_name": "Új domain neve",
"no_cookies_in_domain": "No cookies set for this domain", "no_cookies_in_domain": "Nincs süti beállítva ehhez a domainhez.",
"raw_tab": "Raw", "raw_tab": "Nyers",
"set": "Set a cookie" "set": "Süti beállítása"
} }
}, },
"count": { "count": {
@@ -221,7 +221,7 @@
"generate_message": "Importáljon bármilyen Hoppscotch-gyűjteményt, hogy API-dokumentációt készítsen a folyamat során." "generate_message": "Importáljon bármilyen Hoppscotch-gyűjteményt, hogy API-dokumentációt készítsen a folyamat során."
}, },
"empty": { "empty": {
"authorization": "Ez a kérés nem használ felhatalmazást", "authorization": "Ez a kérés nem használ azonosítást",
"body": "Ennek a kérésnek nincs törzse", "body": "Ennek a kérésnek nincs törzse",
"collection": "A gyűjtemény üres", "collection": "A gyűjtemény üres",
"collections": "A gyűjtemények üresek", "collections": "A gyűjtemények üresek",
@@ -252,39 +252,39 @@
"create_new": "Új környezet létrehozása", "create_new": "Új környezet létrehozása",
"created": "Környezet létrehozva", "created": "Környezet létrehozva",
"deleted": "Környezet törlése", "deleted": "Környezet törlése",
"duplicated": "Environment duplicated", "duplicated": "Környezet duplikálása",
"edit": "Környezet szerkesztése", "edit": "Környezet szerkesztése",
"empty_variables": "No variables", "empty_variables": "Nincs változó",
"global": "Global", "global": "Globális",
"global_variables": "Global variables", "global_variables": "Globális változók",
"import_or_create": "Import or create a environment", "import_or_create": "Környezet importálása vagy létrehozása",
"invalid_name": "Adjon nevet a környezetnek", "invalid_name": "Adjon nevet a környezetnek",
"list": "Environment variables", "list": "Környezeti változók",
"my_environments": "Saját környezetek", "my_environments": "Saját környezetek",
"name": "Name", "name": "Név",
"nested_overflow": "az egymásba ágyazott környezeti változók 10 szintre vannak korlátozva", "nested_overflow": "az egymásba ágyazott környezeti változók 10 szintre vannak korlátozva",
"new": "Új környezet", "new": "Új környezet",
"no_active_environment": "No active environment", "no_active_environment": "Nincs aktív környezet",
"no_environment": "Nincs környezet", "no_environment": "Nincs környezet",
"no_environment_description": "Nem lettek környezetek kiválasztva. Válassza ki, hogy mit kell tenni a következő változókkal.", "no_environment_description": "Nem lettek környezetek kiválasztva. Válassza ki, hogy mit kell tenni a következő változókkal.",
"quick_peek": "Environment Quick Peek", "quick_peek": "Környezet gyors megnézése",
"replace_with_variable": "Replace with variable", "replace_with_variable": "Cserélje le egy változóra",
"scope": "Scope", "scope": "Hatókör",
"select": "Környezet kiválasztása", "select": "Környezet kiválasztása",
"set": "Set environment", "set": "Környezet beállítása",
"set_as_environment": "Set as environment", "set_as_environment": "Környezetként való beállítás",
"team_environments": "Csapatkörnyezetek", "team_environments": "Csapatkörnyezetek",
"title": "Környezetek", "title": "Környezetek",
"updated": "Környezet frissítve", "updated": "Környezet frissítve",
"value": "Value", "value": "Érték",
"variable": "Variable", "variable": "Változó",
"variable_list": "Változólista" "variable_list": "Változólista"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers", "authproviders_load_error": "Nem sikerült betölteni az azonosító szolgáltatókat",
"browser_support_sse": "Úgy tűnik, hogy ez a böngésző nem támogatja a kiszolgáló által küldött eseményeket.", "browser_support_sse": "Úgy tűnik, hogy ez a böngésző nem támogatja a kiszolgáló által küldött eseményeket.",
"check_console_details": "Nézze meg a konzolnaplót a részletekért.", "check_console_details": "Nézze meg a konzolnaplót a részletekért.",
"check_how_to_add_origin": "Check how you can add an origin", "check_how_to_add_origin": "Ellenőrizze, hogy hogyan adhat hozzá forrást",
"curl_invalid_format": "A cURL nincs megfelelően formázva", "curl_invalid_format": "A cURL nincs megfelelően formázva",
"danger_zone": "Veszélyes zóna", "danger_zone": "Veszélyes zóna",
"delete_account": "Az Ön fiókja jelenleg tulajdonos ezekben a csapatokban:", "delete_account": "Az Ön fiókja jelenleg tulajdonos ezekben a csapatokban:",
@@ -300,13 +300,13 @@
"json_prettify_invalid_body": "Nem sikerült csinosítani egy érvénytelen törzset, oldja meg a JSON szintaktikai hibáit, és próbálja újra", "json_prettify_invalid_body": "Nem sikerült csinosítani egy érvénytelen törzset, oldja meg a JSON szintaktikai hibáit, és próbálja újra",
"network_error": "Úgy tűnik, hogy hálózati hiba van. Próbálja újra.", "network_error": "Úgy tűnik, hogy hálózati hiba van. Próbálja újra.",
"network_fail": "Nem sikerült elküldeni a kérést", "network_fail": "Nem sikerült elküldeni a kérést",
"no_collections_to_export": "No collections to export. Please create a collection to get started.", "no_collections_to_export": "Nincs exportálható gyűjtemény. Kérjük, hozzon létre egyet, hogy elkezdhesse.",
"no_duration": "Nincs időtartam", "no_duration": "Nincs időtartam",
"no_environments_to_export": "No environments to export. Please create an environment to get started.", "no_environments_to_export": "Nincs exportálható környezet. Kérjük, hozzon létre egyet, hogy elkezdhesse.",
"no_results_found": "Nincs találat", "no_results_found": "Nincs találat",
"page_not_found": "Ez az oldal nem található", "page_not_found": "Ez az oldal nem található",
"please_install_extension": "Please install the extension and add origin to the extension.", "please_install_extension": "Kérjük telepítse a bővítményt és adja hozzá a forráshoz.",
"proxy_error": "Proxy error", "proxy_error": "Proxy hiba",
"script_fail": "Nem sikerült végrehajtani a kérés előtti parancsfájlt", "script_fail": "Nem sikerült végrehajtani a kérés előtti parancsfájlt",
"something_went_wrong": "Valami elromlott", "something_went_wrong": "Valami elromlott",
"test_script_fail": "Nem sikerült végrehajtani a kérés utáni parancsfájlt" "test_script_fail": "Nem sikerült végrehajtani a kérés utáni parancsfájlt"
@@ -314,7 +314,7 @@
"export": { "export": {
"as_json": "Exportálás JSON formátumban", "as_json": "Exportálás JSON formátumban",
"create_secret_gist": "Titkos Gist létrehozása", "create_secret_gist": "Titkos Gist létrehozása",
"failed": "Something went wrong while exporting", "failed": "Valami hiba történt az exportálás közben",
"gist_created": "Gist létrehozva", "gist_created": "Gist létrehozva",
"require_github": "Jelentkezzen be GitHub használatával a titkos Gist létrehozásához", "require_github": "Jelentkezzen be GitHub használatával a titkos Gist létrehozásához",
"title": "Exportálás" "title": "Exportálás"
@@ -333,16 +333,16 @@
"renamed": "Mappa átnevezve" "renamed": "Mappa átnevezve"
}, },
"graphql": { "graphql": {
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", "connection_switch_confirm": "Szeretne csatlakozni a legújabb GraphQL végponttal?",
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", "connection_switch_new_url": "A tab váltása lecsatlakoztatta az aktív GraphQL kapcsolatról. Az új kapcsolat",
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", "connection_switch_url": "Kapcsolódott a GraphQL végponthoz. A kapcsolat",
"mutations": "Mutációk", "mutations": "Mutációk",
"schema": "Séma", "schema": "Séma",
"subscriptions": "Feliratkozások", "subscriptions": "Feliratkozások",
"switch_connection": "Switch connection" "switch_connection": "Kapcsolat váltása"
}, },
"graphql_collections": { "graphql_collections": {
"title": "GraphQL Collections" "title": "GraphQL gyűjtemény"
}, },
"group": { "group": {
"time": "Idő", "time": "Idő",
@@ -354,9 +354,9 @@
"save_workspace": "Saját munkaterület mentése" "save_workspace": "Saját munkaterület mentése"
}, },
"helpers": { "helpers": {
"authorization": "A felhatalmazási fejléc automatikusan elő lesz állítva a kérés elküldésekor.", "authorization": "Az Azonosítás fejléc automatikusan elő lesz állítva a kérés elküldésekor.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.", "collection_properties_authorization": " Ez az azonosítás be lesz állítva minden kéréshez ebben a gyűjteményben.",
"collection_properties_header": "This header will be set for every request in this collection.", "collection_properties_header": "Ez a fejléc be lesz állítva mint minden kéréshez ebben a gyűjteményben.",
"generate_documentation_first": "Először állítsa elő a dokumentációt", "generate_documentation_first": "Először állítsa elő a dokumentációt",
"network_fail": "Nem lehet elérni az API-végpontot. Ellenőrizze a hálózati kapcsolatot vagy válasszon egy másik elfogót, és próbálja újra.", "network_fail": "Nem lehet elérni az API-végpontot. Ellenőrizze a hálózati kapcsolatot vagy válasszon egy másik elfogót, és próbálja újra.",
"offline": "Úgy tűnik, hogy kapcsolat nélküli módban van. Előfordulhat, hogy a munkaterületen lévő adatok nem naprakészek.", "offline": "Úgy tűnik, hogy kapcsolat nélküli módban van. Előfordulhat, hogy a munkaterületen lévő adatok nem naprakészek.",
@@ -376,11 +376,11 @@
"import": { "import": {
"collections": "Gyűjtemények importálása", "collections": "Gyűjtemények importálása",
"curl": "cURL importálása", "curl": "cURL importálása",
"environments_from_gist": "Import From Gist", "environments_from_gist": "Importálás Gist-ből",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist", "environments_from_gist_description": "Hoppscotch környezetek importálása Gist-ből",
"failed": "Hiba az importálás során: a formátum nem azonosítható", "failed": "Hiba az importálás során: a formátum nem azonosítható",
"from_file": "Import from File", "from_file": "Importálás fájlból",
"from_gist": "Importálás Gistből", "from_gist": "Importálás Gist-ből",
"from_gist_description": "Importálás Gist URL-ből", "from_gist_description": "Importálás Gist URL-ből",
"from_insomnia": "Importálás Insomniából", "from_insomnia": "Importálás Insomniából",
"from_insomnia_description": "Importálás Insomnia-gyűjteményből", "from_insomnia_description": "Importálás Insomnia-gyűjteményből",
@@ -390,45 +390,45 @@
"from_my_collections_description": "Importálás saját gyűjtemények fájlból", "from_my_collections_description": "Importálás saját gyűjtemények fájlból",
"from_openapi": "Importálás OpenAPI-ból", "from_openapi": "Importálás OpenAPI-ból",
"from_openapi_description": "Importálás OpenAPI specifikációs fájlból (YML/JSON)", "from_openapi_description": "Importálás OpenAPI specifikációs fájlból (YML/JSON)",
"from_postman": "Importálás Postmanből", "from_postman": "Importálás Postman-ből",
"from_postman_description": "Importálás Postman-gyűjteményből", "from_postman_description": "Importálás Postman-gyűjteményből",
"from_url": "Importálás URL-ből", "from_url": "Importálás URL-ből",
"gist_url": "Gist URL megadása", "gist_url": "Gist URL megadása",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist", "gql_collections_from_gist_description": "GraphQL gyűjtemények importálása Gist-ből",
"hoppscotch_environment": "Hoppscotch Environment", "hoppscotch_environment": "Hoppscotch környezet",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", "hoppscotch_environment_description": "Hoppscotch környezet importálása JSON fájlból",
"import_from_url_invalid_fetch": "Nem sikerült lekérni az adatokat az URL-ről", "import_from_url_invalid_fetch": "Nem sikerült lekérni az adatokat az URL-ről",
"import_from_url_invalid_file_format": "Hiba a gyűjtemények importálása során", "import_from_url_invalid_file_format": "Hiba a gyűjtemények importálása során",
"import_from_url_invalid_type": "Nem támogatott típus. Az elfogadott értékek: „hoppscotch”, „openapi”, „postman” vagy „insomnia”.", "import_from_url_invalid_type": "Nem támogatott típus. Az elfogadott értékek: „hoppscotch”, „openapi”, „postman” vagy „insomnia”.",
"import_from_url_success": "Gyűjtemények importálva", "import_from_url_success": "Gyűjtemények importálva",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", "insomnia_environment_description": "Insomnia környezet importálása JSON/YAML fájlból",
"json_description": "Gyűjtemények importálása Hoppscotch-gyűjtemények JSON-fájlból", "json_description": "Gyűjtemények importálása Hoppscotch-gyűjtemények JSON-fájlból",
"postman_environment": "Postman Environment", "postman_environment": "Postman környezet",
"postman_environment_description": "Import Postman Environment from a JSON file", "postman_environment_description": "Postman környezet importálása JSON fájlból",
"title": "Importálás" "title": "Importálás"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Lehetséges hibák ellenőrzése",
"environment": { "environment": {
"add_environment": "Add to Environment", "add_environment": "Hozzáadás a környezethez",
"not_found": "Environment variable “{environment}” not found." "not_found": "\"{environment}\" környezet nem található."
}, },
"header": { "header": {
"cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." "cookie": "A böngésző nem engedélyezi a Hoppscotch-nak, hogy süti fejlécet állítson be. Amíg dolgozunk a Hoppscotch asztali alkalmazáson (hamarosan), kérjük használjon Authorization Header fejlécet."
}, },
"response": { "response": {
"401_error": "Please check your authentication credentials.", "401_error": "Kérjük ellenőrizze az autentikációs adatokat.",
"404_error": "Please check your request URL and method type.", "404_error": "Kérjük ellenőrizze a kérés URL-jét és típusát.",
"cors_error": "Please check your Cross-Origin Resource Sharing configuration.", "cors_error": "Kérjük ellenőrizze a Cross-Origin Resource Sharing beállítást.",
"default_error": "Please check your request.", "default_error": "Kérjük ellenőrizze a kérését.",
"network_error": "Please check your network connection." "network_error": "Kérjük ellenőrizze a internet elérhetőségét."
}, },
"title": "Inspector", "title": "Inspector",
"url": { "url": {
"extension_not_installed": "Extension not installed.", "extension_not_installed": "Bővítmény nincs telepítve.",
"extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", "extension_unknown_origin": "Ellenőrizze, hogy hozzáadta az API végpont forrását Hoppscotch Browser bővítmény listájához.",
"extention_enable_action": "Enable Browser Extension", "extention_enable_action": "Bővítmény engedélyezése",
"extention_not_enabled": "Extension not enabled." "extention_not_enabled": "Bővítmény nincs engedélyezve."
} }
}, },
"layout": { "layout": {
@@ -442,10 +442,10 @@
"close_unsaved_tab": "Elmentetlen változtatásai vannak", "close_unsaved_tab": "Elmentetlen változtatásai vannak",
"collections": "Gyűjtemények", "collections": "Gyűjtemények",
"confirm": "Megerősítés", "confirm": "Megerősítés",
"customize_request": "Customize Request", "customize_request": "Kérés testreszabása",
"edit_request": "Kérés szerkesztése", "edit_request": "Kérés szerkesztése",
"import_export": "Importálás és exportálás", "import_export": "Importálás és exportálás",
"share_request": "Share Request" "share_request": "Kérés megosztása"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "Ön már feliratkozott erre a témára.", "already_subscribed": "Ön már feliratkozott erre a témára.",
@@ -511,7 +511,7 @@
}, },
"request": { "request": {
"added": "Kérés hozzáadva", "added": "Kérés hozzáadva",
"authorization": "Felhatalmazás", "authorization": "Azonosítás",
"body": "Kérés törzse", "body": "Kérés törzse",
"choose_language": "Nyelv kiválasztása", "choose_language": "Nyelv kiválasztása",
"content_type": "Tartalom típusa", "content_type": "Tartalom típusa",
@@ -526,8 +526,8 @@
"enter_curl": "cURL-parancs megadása", "enter_curl": "cURL-parancs megadása",
"generate_code": "Kód előállítása", "generate_code": "Kód előállítása",
"generated_code": "Előállított kód", "generated_code": "Előállított kód",
"go_to_authorization_tab": "Go to Authorization tab", "go_to_authorization_tab": "Navigálás az Azonosítás lapra",
"go_to_body_tab": "Go to Body tab", "go_to_body_tab": "Navigálás a Törzs lapra.",
"header_list": "Fejléclista", "header_list": "Fejléclista",
"invalid_name": "Adjon nevet a kérésnek", "invalid_name": "Adjon nevet a kérésnek",
"method": "Módszer", "method": "Módszer",
@@ -552,8 +552,8 @@
"saved": "Kérés elmentve", "saved": "Kérés elmentve",
"share": "Megosztás", "share": "Megosztás",
"share_description": "A Hoppscotch megosztása az ismerőseivel", "share_description": "A Hoppscotch megosztása az ismerőseivel",
"share_request": "Share Request", "share_request": "Kérés megosztása",
"stop": "Stop", "stop": "Leállítás",
"title": "Kérés", "title": "Kérés",
"type": "Kérés típusa", "type": "Kérés típusa",
"url": "URL", "url": "URL",
@@ -587,7 +587,7 @@
"account_description": "A fiókbeállítások személyre szabása.", "account_description": "A fiókbeállítások személyre szabása.",
"account_email_description": "Az Ön elsődleges e-mail-címe.", "account_email_description": "Az Ön elsődleges e-mail-címe.",
"account_name_description": "Ez a megjelenített neve.", "account_name_description": "Ez a megjelenített neve.",
"additional": "Additional Settings", "additional": "További beállítások",
"background": "Háttér", "background": "Háttér",
"black_mode": "Fekete", "black_mode": "Fekete",
"choose_language": "Nyelv kiválasztása", "choose_language": "Nyelv kiválasztása",
@@ -635,29 +635,29 @@
"verify_email": "E-mail-cím ellenőrzése" "verify_email": "E-mail-cím ellenőrzése"
}, },
"shared_requests": { "shared_requests": {
"button": "Button", "button": "Gomb",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "button_info": "Hozza létre a 'Futtatás Hoppscotch-ban' gombot a weboldalán vagy blogján.",
"copy_html": "Copy HTML", "copy_html": "HTML másolása",
"copy_link": "Copy Link", "copy_link": "Link másolása",
"copy_markdown": "Copy Markdown", "copy_markdown": "Jelölő másolása",
"creating_widget": "Creating widget", "creating_widget": "Widget létrehozása",
"customize": "Customize", "customize": "Testreszabás",
"deleted": "Shared request deleted", "deleted": "Megosztott kérés törölve",
"description": "Select a widget, you can change and customize this later", "description": "Válasszon ki egy widgetet, ezt később módosíthatja és testreszabhatja",
"embed": "Embed", "embed": "Beágyazás",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", "embed_info": "Adja hozzá a \"Hoppscotch API Playground\"-ot weboldalához vagy blogjához.",
"link": "Link", "link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.", "link_info": "Link létrehozása olvasási joggal való megosztáshoz.",
"modified": "Shared request modified", "modified": "Megosztott kérés módosítva",
"not_found": "Shared request not found", "not_found": "Megosztott kérés nem található.",
"open_new_tab": "Open in new tab", "open_new_tab": "Megnyitás új lapon.",
"preview": "Preview", "preview": "Előnézet",
"run_in_hoppscotch": "Run in Hoppscotch", "run_in_hoppscotch": "Futtatás Hoppscotch-ban.",
"theme": { "theme": {
"dark": "Dark", "dark": "Sötét",
"light": "Light", "light": "Világos",
"system": "System", "system": "Rendszer",
"title": "Theme" "title": "Téma"
} }
}, },
"shortcut": { "shortcut": {
@@ -669,7 +669,7 @@
"title": "Általános" "title": "Általános"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "Emberek meghívása a Hoppscotchba", "invite": "Emberek meghívása a Hoppscotch-ba",
"title": "Egyebek" "title": "Egyebek"
}, },
"navigation": { "navigation": {
@@ -691,19 +691,19 @@
"delete_method": "DELETE módszer kiválasztása", "delete_method": "DELETE módszer kiválasztása",
"get_method": "GET módszer kiválasztása", "get_method": "GET módszer kiválasztása",
"head_method": "HEAD módszer kiválasztása", "head_method": "HEAD módszer kiválasztása",
"import_curl": "Import cURL", "import_curl": "cURL importálása",
"method": "Módszer", "method": "Módszer",
"next_method": "Következő módszer kiválasztása", "next_method": "Következő módszer kiválasztása",
"post_method": "POST módszer kiválasztása", "post_method": "POST módszer kiválasztása",
"previous_method": "Előző módszer kiválasztása", "previous_method": "Előző módszer kiválasztása",
"put_method": "PUT módszer kiválasztása", "put_method": "PUT módszer kiválasztása",
"rename": "Rename Request", "rename": "Kérés átnevezése",
"reset_request": "Kérés visszaállítása", "reset_request": "Kérés visszaállítása",
"save_request": "Save Request", "save_request": "Kérés mentése",
"save_to_collections": "Mentés a gyűjteményekbe", "save_to_collections": "Mentés a gyűjteményekbe",
"send_request": "Kérés elküldése", "send_request": "Kérés elküldése",
"share_request": "Share Request", "share_request": "Kérés megosztása",
"show_code": "Generate code snippet", "show_code": "Kódrészlet generálása",
"title": "Kérés", "title": "Kérés",
"copy_request_link": "Kérés hivatkozásának másolása" "copy_request_link": "Kérés hivatkozásának másolása"
}, },
@@ -735,82 +735,82 @@
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "Change Language", "change_language": "Nyelv váltása",
"environments": { "environments": {
"delete": "Delete current environment", "delete": "Jelenlegi környezet törlése",
"duplicate": "Duplicate current environment", "duplicate": "Jelenlegi környezet duplikálása",
"duplicate_global": "Duplicate global environment", "duplicate_global": "Globális környezet duplikálása",
"edit": "Edit current environment", "edit": "Jelenlegi környezet szerkesztése",
"edit_global": "Edit global environment", "edit_global": "Globális környezet szerkesztése",
"new": "Create new environment", "new": "Új környezet létrehozása",
"new_variable": "Create a new environment variable", "new_variable": "Új környezeti változó létrehozása",
"title": "Environments" "title": "Környezetek"
}, },
"general": { "general": {
"chat": "Chat with support", "chat": "Üzenet a supportnak",
"help_menu": "Help and support", "help_menu": "Segítség és support",
"open_docs": "Read Documentation", "open_docs": "Dokumentáció olvasása",
"open_github": "Open GitHub repository", "open_github": "GitHub repository megnyitása",
"open_keybindings": "Keyboard shortcuts", "open_keybindings": "Billentyűkombinációk megnyitása",
"social": "Social", "social": "Közösség",
"title": "General" "title": "Általános"
}, },
"graphql": { "graphql": {
"connect": "Connect to server", "connect": "Csatlakozás a szerverhez",
"disconnect": "Disconnect from server" "disconnect": "Lecsatlakozás a szerverről"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "Invite your friends to Hoppscotch", "invite": "Hívja meg barátait a Hoppscotch-ba",
"title": "Miscellaneous" "title": "Egyéb"
}, },
"request": { "request": {
"save_as_new": "Save as new request", "save_as_new": "Mentés új kérésként",
"select_method": "Select method", "select_method": "Módszer kiválasztása",
"switch_to": "Switch to", "switch_to": "Váltás",
"tab_authorization": "Authorization tab", "tab_authorization": "Azonosítás lap",
"tab_body": "Body tab", "tab_body": "Törzs lap",
"tab_headers": "Headers tab", "tab_headers": "Fejlécek lap",
"tab_parameters": "Parameters tab", "tab_parameters": "Paraméterek lap",
"tab_pre_request_script": "Pre-request script tab", "tab_pre_request_script": "Előzetes script lap",
"tab_query": "Query tab", "tab_query": "Kérés lap",
"tab_tests": "Tests tab", "tab_tests": "Tesztek lap",
"tab_variables": "Variables tab" "tab_variables": "Változók lap"
}, },
"response": { "response": {
"copy": "Copy response", "copy": "Válasz másolása",
"download": "Download response as file", "download": "Válasz letöltése fájlként",
"title": "Response" "title": "Válasz"
}, },
"section": { "section": {
"interceptor": "Interceptor", "interceptor": "Interceptor",
"interface": "Interface", "interface": "Interface",
"theme": "Theme", "theme": "Téma",
"user": "User" "user": "Felhasználó"
}, },
"settings": { "settings": {
"change_interceptor": "Change Interceptor", "change_interceptor": "Interceptor váltása",
"change_language": "Change Language", "change_language": "Nyelv váltása",
"theme": { "theme": {
"black": "Black", "black": "Fekete",
"dark": "Dark", "dark": "Sötét",
"light": "Light", "light": "Világos",
"system": "System preference" "system": "Rendszer"
} }
}, },
"tab": { "tab": {
"close_current": "Close current tab", "close_current": "Jelenlegi lap bezására",
"close_others": "Close all other tabs", "close_others": "Összes többi lap bezására",
"duplicate": "Duplicate current tab", "duplicate": "Jelenlegi lap diplikálása",
"new_tab": "Open a new tab", "new_tab": "Új lap megnyitása",
"title": "Tabs" "title": "Lapok"
}, },
"workspace": { "workspace": {
"delete": "Delete current team", "delete": "Jelenlegi csapat törlése",
"edit": "Edit current team", "edit": "Jelenlegi csapat szerkesztése",
"invite": "Invite people to team", "invite": "Emberek meghívása a jelenlegi csapatba",
"new": "Create new team", "new": "Új csapat létrehozása",
"switch_to_personal": "Switch to your personal workspace", "switch_to_personal": "Váltás a személyes munkaterületére",
"title": "Teams" "title": "Csapatok"
} }
}, },
"sse": { "sse": {
@@ -828,20 +828,20 @@
"connection_error": "Nem sikerült kapcsolódni", "connection_error": "Nem sikerült kapcsolódni",
"connection_failed": "A kapcsolódás sikertelen", "connection_failed": "A kapcsolódás sikertelen",
"connection_lost": "A kapcsolat elveszett", "connection_lost": "A kapcsolat elveszett",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard", "copied_interface_to_clipboard": "{language} interface típusa vágólapra másolva",
"copied_to_clipboard": "Vágólapra másolva", "copied_to_clipboard": "Vágólapra másolva",
"deleted": "Törölve", "deleted": "Törölve",
"deprecated": "ELAVULT", "deprecated": "ELAVULT",
"disabled": "Letiltva", "disabled": "Letiltva",
"disconnected": "Leválasztva", "disconnected": "Lecsatlakoztatva",
"disconnected_from": "Leválasztva innen: {name}", "disconnected_from": "Lecsatlakoztatva innen: {name}",
"docs_generated": "Dokumentáció előállítva", "docs_generated": "Dokumentáció előállítva",
"download_failed": "Download failed", "download_failed": "Letöltés sikertelen",
"download_started": "A letöltés elkezdődött", "download_started": "A letöltés elkezdődött",
"enabled": "Engedélyezve", "enabled": "Engedélyezve",
"file_imported": "Fájl importálva", "file_imported": "Fájl importálva",
"finished_in": "Befejeződött {duration} ms alatt", "finished_in": "Befejeződött {duration} ms alatt",
"hide": "Hide", "hide": "Elrejtés",
"history_deleted": "Előzmények törölve", "history_deleted": "Előzmények törölve",
"linewrap": "Sorok tördelése", "linewrap": "Sorok tördelése",
"loading": "Betöltés…", "loading": "Betöltés…",
@@ -872,13 +872,13 @@
"twitter": "Kövessen minket Twitteren" "twitter": "Kövessen minket Twitteren"
}, },
"tab": { "tab": {
"authorization": "Felhatalmazás", "authorization": "Azonosítás",
"body": "Törzs", "body": "Törzs",
"close": "Close Tab", "close": "Lap bezárása",
"close_others": "Close other Tabs", "close_others": "Többi lap bezárása",
"collections": "Gyűjtemények", "collections": "Gyűjtemények",
"documentation": "Dokumentáció", "documentation": "Dokumentáció",
"duplicate": "Duplicate Tab", "duplicate": "Lap duplikálása",
"environments": "Környezetek", "environments": "Környezetek",
"headers": "Fejlécek", "headers": "Fejlécek",
"history": "Előzmények", "history": "Előzmények",
@@ -905,7 +905,7 @@
"email_do_not_match": "Az e-mail-cím nem egyezik a fiókja részleteivel. Vegye fel a kapcsolatot a csapat tulajdonosával.", "email_do_not_match": "Az e-mail-cím nem egyezik a fiókja részleteivel. Vegye fel a kapcsolatot a csapat tulajdonosával.",
"exit": "Kilépés a csapatból", "exit": "Kilépés a csapatból",
"exit_disabled": "Csak a tulajdonos nem léphet ki a csapatból", "exit_disabled": "Csak a tulajdonos nem léphet ki a csapatból",
"failed_invites": "Failed invites", "failed_invites": "Hiba a meghívás közben",
"invalid_coll_id": "Érvénytelen gyűjteményazonosító", "invalid_coll_id": "Érvénytelen gyűjteményazonosító",
"invalid_email_format": "Az e-mail formátuma érvénytelen", "invalid_email_format": "Az e-mail formátuma érvénytelen",
"invalid_id": "Érvénytelen csapatazonosító. Vegye fel a kapcsolatot a csapat tulajdonosával.", "invalid_id": "Érvénytelen csapatazonosító. Vegye fel a kapcsolatot a csapat tulajdonosával.",

File diff suppressed because it is too large Load Diff

View File

@@ -11,12 +11,13 @@
"connect": "Подключиться", "connect": "Подключиться",
"connecting": "Соединение...", "connecting": "Соединение...",
"copy": "Скопировать", "copy": "Скопировать",
"create": "Create", "create": "Создать",
"delete": "Удалить", "delete": "Удалить",
"disconnect": "Отключиться", "disconnect": "Отключиться",
"dismiss": "Скрыть", "dismiss": "Скрыть",
"dont_save": "Не сохранять", "dont_save": "Не сохранять",
"download_file": "Скачать файл", "download_file": "Скачать файл",
"download_here": "Download here",
"drag_to_reorder": "Перетягивайте для сортировки", "drag_to_reorder": "Перетягивайте для сортировки",
"duplicate": "Дублировать", "duplicate": "Дублировать",
"edit": "Редактировать", "edit": "Редактировать",
@@ -24,6 +25,7 @@
"go_back": "Вернуться", "go_back": "Вернуться",
"go_forward": "Вперёд", "go_forward": "Вперёд",
"group_by": "Сгруппировать по", "group_by": "Сгруппировать по",
"hide_secret": "Hide secret",
"label": "Название", "label": "Название",
"learn_more": "Узнать больше", "learn_more": "Узнать больше",
"less": "Меньше", "less": "Меньше",
@@ -33,7 +35,7 @@
"open_workspace": "Открыть пространство", "open_workspace": "Открыть пространство",
"paste": "Вставить", "paste": "Вставить",
"prettify": "Форматировать", "prettify": "Форматировать",
"properties": "Properties", "properties": "Параметры",
"remove": "Удалить", "remove": "Удалить",
"rename": "Переименовать", "rename": "Переименовать",
"restore": "Восстановить", "restore": "Восстановить",
@@ -42,13 +44,14 @@
"scroll_to_top": "Вверх", "scroll_to_top": "Вверх",
"search": "Поиск", "search": "Поиск",
"send": "Отправить", "send": "Отправить",
"share": "Share", "share": "Поделиться",
"show_secret": "Show secret",
"start": "Начать", "start": "Начать",
"starting": "Запускаю", "starting": "Запускаю",
"stop": "Стоп", "stop": "Стоп",
"to_close": "что бы закрыть", "to_close": "закрыть",
"to_navigate": "для навигации", "to_navigate": "для навигации",
"to_select": "выборать", "to_select": "выбрать",
"turn_off": "Выключить", "turn_off": "Выключить",
"turn_on": "Включить", "turn_on": "Включить",
"undo": "Отменить", "undo": "Отменить",
@@ -66,12 +69,12 @@
"copy_interface_type": "Copy interface type", "copy_interface_type": "Copy interface type",
"copy_user_id": "Копировать токен пользователя", "copy_user_id": "Копировать токен пользователя",
"developer_option": "Настройки разработчика", "developer_option": "Настройки разработчика",
"developer_option_description": "Инструмент разработчика помогает обслуживить и развивить Hoppscotch", "developer_option_description": "Инструмент разработчика помогает обслуживать и развивать Hoppscotch",
"discord": "Discord", "discord": "Discord",
"documentation": "Документация", "documentation": "Документация",
"github": "GitHub", "github": "GitHub",
"help": "Справка, отзывы и документация", "help": "Справка, отзывы и документация",
"home": "Дом", "home": "На главную",
"invite": "Пригласить", "invite": "Пригласить",
"invite_description": "В Hoppscotch мы разработали простой и интуитивно понятный интерфейс для создания и управления вашими API. Hoppscotch - это инструмент, который помогает создавать, тестировать, документировать и делиться своими API.", "invite_description": "В Hoppscotch мы разработали простой и интуитивно понятный интерфейс для создания и управления вашими API. Hoppscotch - это инструмент, который помогает создавать, тестировать, документировать и делиться своими API.",
"invite_your_friends": "Пригласить своих друзей", "invite_your_friends": "Пригласить своих друзей",
@@ -85,7 +88,7 @@
"reload": "Перезагрузить", "reload": "Перезагрузить",
"search": "Поиск", "search": "Поиск",
"share": "Поделиться", "share": "Поделиться",
"shortcuts": "Ярлыки", "shortcuts": "Горячие клавиши",
"social_description": "Подписывайся на наши соц. сети и оставайся всегда в курсе последних новостей, обновлений и релизов.", "social_description": "Подписывайся на наши соц. сети и оставайся всегда в курсе последних новостей, обновлений и релизов.",
"social_links": "Социальные сети", "social_links": "Социальные сети",
"spotlight": "Прожектор", "spotlight": "Прожектор",
@@ -96,17 +99,19 @@
"type_a_command_search": "Введите команду или выполните поиск…", "type_a_command_search": "Введите команду или выполните поиск…",
"we_use_cookies": "Мы используем куки", "we_use_cookies": "Мы используем куки",
"whats_new": "Что нового?", "whats_new": "Что нового?",
"wiki": "Вики" "wiki": "Узнать больше"
}, },
"auth": { "auth": {
"account_exists": "Учетная запись существует с разными учетными данными - войдите, чтобы связать обе учетные записи", "account_exists": "Учетная запись существует с разными учетными данными - войдите, чтобы связать обе учетные записи",
"all_sign_in_options": "Все варианты входа", "all_sign_in_options": "Все варианты входа",
"continue_with_auth_provider": "Continue with {provider}",
"continue_with_email": "Продолжить с электронной почтой", "continue_with_email": "Продолжить с электронной почтой",
"continue_with_github": "Продолжить с GitHub", "continue_with_github": "Продолжить с GitHub",
"continue_with_github_enterprise": "Continue with GitHub Enterprise",
"continue_with_google": "Продолжить с Google", "continue_with_google": "Продолжить с Google",
"continue_with_microsoft": "Продолжить с Microsoft", "continue_with_microsoft": "Продолжить с Microsoft",
"email": "Электронное письмо", "email": "Электронное письмо",
"logged_out": "Вышли из", "logged_out": "Успешно вышли. Будем скучать!",
"login": "Авторизоваться", "login": "Авторизоваться",
"login_success": "Успешный вход в систему", "login_success": "Успешный вход в систему",
"login_to_hoppscotch": "Войти в Hoppscotch", "login_to_hoppscotch": "Войти в Hoppscotch",
@@ -121,7 +126,7 @@
"generate_token": "Сгенерировать токен", "generate_token": "Сгенерировать токен",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Добавить в URL", "include_in_url": "Добавить в URL",
"inherited_from": "Inherited {auth} from parent collection {collection} ", "inherited_from": "Унаследован тип аутентификации {auth} из родительской коллекции {collection}",
"learn": "Узнать больше", "learn": "Узнать больше",
"oauth": { "oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Auth Server returned an error state",
@@ -135,11 +140,32 @@
"redirect_no_token_endpoint": "No Token Endpoint Defined", "redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation", "something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed",
"grant_type": "Grant Type",
"grant_type_auth_code": "Authorization Code",
"token_fetched_successfully": "Token fetched successfully",
"token_fetch_failed": "Failed to fetch token",
"validation_failed": "Validation Failed, please check the form fields",
"label_authorization_endpoint": "Authorization Endpoint",
"label_client_id": "Client ID",
"label_client_secret": "Client Secret",
"label_code_challenge": "Code Challenge",
"label_code_challenge_method": "Code Challenge Method",
"label_code_verifier": "Code Verifier",
"label_scopes": "Scopes",
"label_token_endpoint": "Token Endpoint",
"label_use_pkce": "Use PKCE",
"label_implicit": "Implicit",
"label_password": "Password",
"label_username": "Username",
"label_auth_code": "Authorization Code",
"label_client_credentials": "Client Credentials"
}, },
"pass_by_headers_label": "Headers",
"pass_by_query_params_label": "Query Parameters",
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Пароль", "password": "Пароль",
"save_to_inherit": "Please save this request in any collection to inherit the authorization", "save_to_inherit": "Чтобы унаследовать аутентификации, нужно сохранить запрос в коллекции",
"token": "Токен", "token": "Токен",
"type": "Метод авторизации", "type": "Метод авторизации",
"username": "Имя пользователя" "username": "Имя пользователя"
@@ -149,6 +175,7 @@
"different_parent": "Нельзя сортировать коллекцию с разной родительской коллекцией", "different_parent": "Нельзя сортировать коллекцию с разной родительской коллекцией",
"edit": "Редактировать коллекцию", "edit": "Редактировать коллекцию",
"import_or_create": "Вы можете импортировать существующую или создать новую коллекцию", "import_or_create": "Вы можете импортировать существующую или создать новую коллекцию",
"import_collection": "Импортировать коллекцию",
"invalid_name": "Укажите допустимое название коллекции", "invalid_name": "Укажите допустимое название коллекции",
"invalid_root_move": "Коллекция уже в корне", "invalid_root_move": "Коллекция уже в корне",
"moved": "Перемещено успешно", "moved": "Перемещено успешно",
@@ -157,38 +184,36 @@
"name_length_insufficient": "Имя коллекции должно иметь 3 или более символов", "name_length_insufficient": "Имя коллекции должно иметь 3 или более символов",
"new": "Создать коллекцию", "new": "Создать коллекцию",
"order_changed": "Порядок коллекции обновлён", "order_changed": "Порядок коллекции обновлён",
"properties": "Collection Properties", "properties": "Параметры коллекции",
"properties_updated": "Collection Properties Updated", "properties_updated": "Параметры коллекции обновлены",
"renamed": "Коллекция переименована", "renamed": "Коллекция переименована",
"request_in_use": "Запрос обрабатывается", "request_in_use": "Запрос обрабатывается",
"save_as": "Сохранить как", "save_as": "Сохранить как",
"save_to_collection": "Сохранить в коллекцию", "save_to_collection": "Сохранить в коллекцию",
"select": "Выбрать коллекцию", "select": "Выбрать коллекцию",
"select_location": "Выберите местоположение", "select_location": "Выберите местоположение"
"select_team": "Выберите команду",
"team_collections": "Коллекции команд"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "Вы уверены что хотите закрыть эту вкладку?", "close_unsaved_tab": "Вы уверены, что хотите закрыть эту вкладку?",
"close_unsaved_tabs": "Вы уверены что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.", "close_unsaved_tabs": "Вы уверены, что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.",
"exit_team": "Вы точно хотите покинуть эту команду?", "exit_team": "Вы точно хотите покинуть эту команду?",
"logout": "Вы действительно хотите выйти?", "logout": "Вы действительно хотите выйти?",
"remove_collection": "Вы уверены, что хотите навсегда удалить эту коллекцию?", "remove_collection": "Вы уверены, что хотите навсегда удалить эту коллекцию?",
"remove_environment": "Вы действительно хотите удалить эту среду без возможности восстановления?", "remove_environment": "Вы действительно хотите удалить это окружение без возможности восстановления?",
"remove_folder": "Вы уверены, что хотите навсегда удалить эту папку?", "remove_folder": "Вы уверены, что хотите навсегда удалить эту папку?",
"remove_history": "Вы уверены, что хотите навсегда удалить всю историю?", "remove_history": "Вы уверены, что хотите навсегда удалить всю историю?",
"remove_request": "Вы уверены, что хотите навсегда удалить этот запрос?", "remove_request": "Вы уверены, что хотите навсегда удалить этот запрос?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?", "remove_shared_request": "Вы уверены, что хотите навсегда удалить этот запрос?",
"remove_team": "Вы уверены, что хотите удалить эту команду?", "remove_team": "Вы уверены, что хотите удалить эту команду?",
"remove_telemetry": "Вы действительно хотите отказаться от телеметрии?", "remove_telemetry": "Вы действительно хотите отказаться от телеметрии?",
"request_change": "Вы уверены что хотите сбросить текущий запрос, все не сохранённые данные будт утеряны?", "request_change": "Вы уверены, что хотите сбросить текущий запрос, все не сохранённые данные будт утеряны?",
"save_unsaved_tab": "Вы хотите сохранить изменения в этой вкладке?", "save_unsaved_tab": "Вы хотите сохранить изменения в этой вкладке?",
"sync": "Вы уверены, что хотите синхронизировать это рабочее пространство?" "sync": "Вы уверены, что хотите синхронизировать это рабочее пространство?"
}, },
"context_menu": { "context_menu": {
"add_parameters": "Add to parameters", "add_parameters": "Добавить в список параметров",
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Открыть запрос в новом окне",
"set_environment_variable": "Set as variable" "set_environment_variable": "Добавить значение в переменную"
}, },
"cookies": { "cookies": {
"modal": { "modal": {
@@ -227,24 +252,25 @@
"collections": "Коллекции пустые", "collections": "Коллекции пустые",
"documentation": "Подключите GraphQL endpoint, чтобы увидеть документацию.", "documentation": "Подключите GraphQL endpoint, чтобы увидеть документацию.",
"endpoint": "Endpoint не может быть пустым", "endpoint": "Endpoint не может быть пустым",
"environments": "Окружения пусты", "environments": "Переменных окружения нет",
"folder": "Папка пуста", "folder": "Папка пуста",
"headers": "У этого запроса нет заголовков", "headers": "У этого запроса нет заголовков",
"history": "История пуста", "history": "История пуста",
"invites": "Вы еще никого не приглашали", "invites": "Вы еще никого не приглашали",
"members": "В этой команде еще нет участников", "members": "В этой команде еще нет участников",
"parameters": "Этот запрос не имеет параметров", "parameters": "Этот запрос не содержит параметров",
"pending_invites": "Пока что нет ожидающих заявок на вступление в команду", "pending_invites": "Пока что нет ожидающих заявок на вступление в команду",
"profile": "Войдите, чтобы просмотреть свой профиль", "profile": "Войдите, чтобы просмотреть свой профиль",
"protocols": "Протоколы пустые", "protocols": "Протоколы пустые",
"request_variables": "Этот запрос не содержит никаких переменных",
"secret_environments": "Секреты хранятся только на этом устройстве и не синхронизируются с сервером",
"schema": "Подключиться к конечной точке GraphQL", "schema": "Подключиться к конечной точке GraphQL",
"shared_requests": "Shared requests are empty", "shared_requests": "Вы еще не делились запросами с другими",
"shared_requests_logout": "Login to view your shared requests or create a new one", "shared_requests_logout": "Нужно войти, чтобы делиться запросами и управлять ими",
"subscription": "Нет подписок", "subscription": "Нет подписок",
"team_name": "Название команды пусто", "team_name": "Название команды пусто",
"teams": "Команды пустые", "teams": "Команды пустые",
"tests": "Для этого запроса нет тестов", "tests": "Для этого запроса нет тестов"
"shortcodes": "Нет коротких ссылок"
}, },
"environment": { "environment": {
"add_to_global": "Добавить в глобальное окружение", "add_to_global": "Добавить в глобальное окружение",
@@ -252,53 +278,57 @@
"create_new": "Создать новое окружение", "create_new": "Создать новое окружение",
"created": "Окружение создано", "created": "Окружение создано",
"deleted": "Окружение удалено", "deleted": "Окружение удалено",
"duplicated": "Environment duplicated", "duplicated": "Окружение продублировано",
"edit": "Редактировать окружение", "edit": "Редактировать окружение",
"empty_variables": "No variables", "empty_variables": "Переменные еще не добавлены",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Глобальные переменные",
"import_or_create": "Импортировать или создать новое окружение", "import_or_create": "Импортировать или создать новое окружение",
"invalid_name": "Укажите допустимое имя для окружения", "invalid_name": "Укажите допустимое имя для окружения",
"list": "Переменные окружения", "list": "Переменные окружения",
"my_environments": "Мои окружения", "my_environments": "Мои окружения",
"name": "Name", "name": "Имя",
"nested_overflow": "максимальный уровень вложения переменных окружения - 10", "nested_overflow": "максимальный уровень вложения переменных окружения - 10",
"new": "Новая среда", "new": "Новая среда",
"no_active_environment": "Нет активных окружений", "no_active_environment": "Нет активных окружений",
"no_environment": "Нет окружения", "no_environment": "Нет окружения",
"no_environment_description": "Не выбрано окружение, выберите что делать с переменными.", "no_environment_description": "Не выбрано окружение, выберите что делать с переменными.",
"quick_peek": "Environment Quick Peek", "quick_peek": "Быстрый просмотр переменных",
"replace_with_variable": "Replace with variable", "replace_with_variable": "Replace with variable",
"scope": "Scope", "scope": "Scope",
"secrets": "Секретные переменные",
"secret_value": "Секретное значение",
"select": "Выберите среду", "select": "Выберите среду",
"set": "Set environment", "set": "Выбрать окружение",
"set_as_environment": "Set as environment", "set_as_environment": "Поместить значение в переменную",
"team_environments": "Окружения команды", "team_environments": "Окружения команды",
"title": "Окружения", "title": "Окружения",
"updated": "Окружение обновлено", "updated": "Окружение обновлено",
"value": "Value", "value": "Значение",
"variable": "Variable", "variable": "Переменная",
"variables": "Переменные",
"variable_list": "Список переменных" "variable_list": "Список переменных"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers", "authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.", "browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.",
"check_console_details": "Подробности смотрите в журнале консоли.", "check_console_details": "Подробности смотрите в журнале консоли.",
"check_how_to_add_origin": "Инструкция как добавить origin в настройки расширения", "check_how_to_add_origin": "Инструкция как это сделать",
"curl_invalid_format": "cURL неправильно отформатирован", "curl_invalid_format": "cURL неправильно отформатирован",
"danger_zone": "Опасная зона", "danger_zone": "Опасная зона",
"delete_account": "Вы являетесь владельцем этой команды:", "delete_account": "Вы являетесь владельцем этой команды:",
"delete_account_description": "Прежде чем удалить аккаунт вам необходимо либо назначить владельцом другого пользователя, либо удалить команды в которых вы являетесь владельцем.", "delete_account_description": "Прежде чем удалить аккаунт вам необходимо либо назначить владельцом другого пользователя, либо удалить команды в которых вы являетесь владельцем.",
"empty_profile_name": "Имя пользователя не может быть пустым",
"empty_req_name": "Пустое имя запроса", "empty_req_name": "Пустое имя запроса",
"f12_details": "(F12 для подробностей)", "f12_details": "(F12 для подробностей)",
"gql_prettify_invalid_query": "Не удалось определить недопустимый запрос, устранить синтаксические ошибки запроса и повторить попытку.", "gql_prettify_invalid_query": "Не удалось отформатировать, т.к. в запросе есть синтаксические ошибки. Устраните их и повторите попытку.",
"incomplete_config_urls": "Не заполнены URL конфигурации", "incomplete_config_urls": "Не заполнены URL конфигурации",
"incorrect_email": "Не корректный Email", "incorrect_email": "Не корректный Email",
"invalid_link": "Не корректная ссылка", "invalid_link": "Не корректная ссылка",
"invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.", "invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.",
"invalid_embed_link": "The embed does not exist or is invalid.", "invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "Не корректный JSON", "json_parsing_failed": "Не корректный JSON",
"json_prettify_invalid_body": "Не удалось определить недопустимое тело, устранить синтаксические ошибки json и повторить попытку.", "json_prettify_invalid_body": "Не удалось определить формат строки, устраните синтаксические ошибки и повторите попытку.",
"network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.", "network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.",
"network_fail": "Не удалось отправить запрос", "network_fail": "Не удалось отправить запрос",
"no_collections_to_export": "Нечего экспортировать. Для начала нужно создать коллекцию.", "no_collections_to_export": "Нечего экспортировать. Для начала нужно создать коллекцию.",
@@ -306,8 +336,10 @@
"no_environments_to_export": "Нечего экспортировать. Для начала нужно создать переменные окружения.", "no_environments_to_export": "Нечего экспортировать. Для начала нужно создать переменные окружения.",
"no_results_found": "Совпадения не найдены", "no_results_found": "Совпадения не найдены",
"page_not_found": "Эта страница не найдена", "page_not_found": "Эта страница не найдена",
"please_install_extension": "Нужно установить специальное расширение и добавить этот домен как новый origin в настройках расширения.", "please_install_extension": "Ничего страшного. Просто нужно установить специальное расширение в браузере.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"reading_files": "Произошла ошибка при чтении файла или нескольких файлов",
"same_profile_name": "Задано имя пользователя такое же как и было",
"script_fail": "Не удалось выполнить сценарий предварительного запроса", "script_fail": "Не удалось выполнить сценарий предварительного запроса",
"something_went_wrong": "Что-то пошло не так", "something_went_wrong": "Что-то пошло не так",
"test_script_fail": "Не удалось выполнить тестирование запроса" "test_script_fail": "Не удалось выполнить тестирование запроса"
@@ -315,13 +347,12 @@
"export": { "export": {
"as_json": "Экспорт как JSON", "as_json": "Экспорт как JSON",
"create_secret_gist": "Создать секретный Gist", "create_secret_gist": "Создать секретный Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist", "create_secret_gist_tooltip_text": "Экспортировать как секретный Gist",
"failed": "Something went wrong while exporting", "failed": "Произошла ошибка во время экспорта",
"secret_gist_success": "Successfully exported as secret Gist", "secret_gist_success": "Успешно экспортировано как секретный Gist",
"require_github": "Войдите через GitHub, чтобы создать секретную суть", "require_github": "Войдите через GitHub, чтобы создать секретную суть",
"title": "Экспорт", "title": "Экспорт",
"success": "Successfully exported", "success": "Успешно экспортировано"
"gist_created": "Gist создан"
}, },
"filter": { "filter": {
"all": "Все", "all": "Все",
@@ -346,7 +377,7 @@
"switch_connection": "Изменить соединение" "switch_connection": "Изменить соединение"
}, },
"graphql_collections": { "graphql_collections": {
"title": "GraphQL Collections" "title": "Коллекции GraphQL"
}, },
"group": { "group": {
"time": "Время", "time": "Время",
@@ -359,8 +390,8 @@
}, },
"helpers": { "helpers": {
"authorization": "Заголовок авторизации будет автоматически сгенерирован при отправке запроса.", "authorization": "Заголовок авторизации будет автоматически сгенерирован при отправке запроса.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.", "collection_properties_authorization": "Этот заголовок авторизации будет подставляться при каждом запросе в этой коллекции.",
"collection_properties_header": "This header will be set for every request in this collection.", "collection_properties_header": "Этот заголовок будет подставляться при каждом запросе в этой коллекции.",
"generate_documentation_first": "Сначала создайте документацию", "generate_documentation_first": "Сначала создайте документацию",
"network_fail": "Невозможно достичь конечной точки API. Проверьте подключение к сети и попробуйте еще раз.", "network_fail": "Невозможно достичь конечной точки API. Проверьте подключение к сети и попробуйте еще раз.",
"offline": "Кажется, вы не в сети. Данные в этой рабочей области могут быть устаревшими.", "offline": "Кажется, вы не в сети. Данные в этой рабочей области могут быть устаревшими.",
@@ -380,10 +411,12 @@
"import": { "import": {
"collections": "Импортировать коллекции", "collections": "Импортировать коллекции",
"curl": "Импортировать из cURL", "curl": "Импортировать из cURL",
"environments_from_gist": "Import From Gist", "environments_from_gist": "Импортировать из Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist", "environments_from_gist_description": "Импортировать переменные окружения Hoppscotch из Gist",
"failed": "Ошибка импорта", "failed": "Ошибка импорта",
"from_file": "Import from File", "file_size_limit_exceeded_warning_multiple_files": "Выбранные файлы превышают рекомендованный лимит в 10MB. Были импортированы только первые {files}",
"file_size_limit_exceeded_warning_single_file": "Размер выбранного в данный момент файла превышает рекомендуемый лимит в 10 МБ. Пожалуйста, выберите другой файл.",
"from_file": "Импортировать из одного или нескольких файлов",
"from_gist": "Импорт из Gist", "from_gist": "Импорт из Gist",
"from_gist_description": "Импортировать через Gist URL", "from_gist_description": "Импортировать через Gist URL",
"from_insomnia": "Импортировать с Insomnia", "from_insomnia": "Импортировать с Insomnia",
@@ -398,9 +431,9 @@
"from_postman_description": "Импортировать из коллекции Postman", "from_postman_description": "Импортировать из коллекции Postman",
"from_url": "Импортировать из URL", "from_url": "Импортировать из URL",
"gist_url": "Введите URL-адрес Gist", "gist_url": "Введите URL-адрес Gist",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist", "gql_collections_from_gist_description": "Импортировать GraphQL коллекцию из Gist",
"hoppscotch_environment": "Hoppscotch Environment", "hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", "hoppscotch_environment_description": "Импортировать окружение Hoppscotch из JSON файла",
"import_from_url_invalid_fetch": "Не удалить получить данные по этому URL", "import_from_url_invalid_fetch": "Не удалить получить данные по этому URL",
"import_from_url_invalid_file_format": "Ошибка при импорте коллекций", "import_from_url_invalid_file_format": "Ошибка при импорте коллекций",
"import_from_url_invalid_type": "Неподдерживаемый тип. Поддерживаемые типы: 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Неподдерживаемый тип. Поддерживаемые типы: 'hoppscotch', 'openapi', 'postman', 'insomnia'",
@@ -409,16 +442,19 @@
"json_description": "Импортировать из коллекции Hoppscotch", "json_description": "Импортировать из коллекции Hoppscotch",
"postman_environment": "Postman Environment", "postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file", "postman_environment_description": "Import Postman Environment from a JSON file",
"success": "Успешно импортировано",
"title": "Импортировать" "title": "Импортировать"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Показать возможные ошибки",
"environment": { "environment": {
"add_environment": "Add to Environment", "add_environment": "Добавить переменную",
"not_found": "Environment variable “{environment}” not found." "add_environment_value": "Заполнить значение",
"empty_value": "Значение переменной окружения '{variable}' пустое",
"not_found": "Переменная окружения “{environment}” не задана."
}, },
"header": { "header": {
"cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." "cookie": "Из-за ограничений безопасности в веб версии нельзя задать Cookie параметры. Пожалуйста, используйте Hoppscotch Desktop приложение или используйте заголовок Authorization вместо этого."
}, },
"response": { "response": {
"401_error": "Please check your authentication credentials.", "401_error": "Please check your authentication credentials.",
@@ -427,12 +463,12 @@
"default_error": "Please check your request.", "default_error": "Please check your request.",
"network_error": "Please check your network connection." "network_error": "Please check your network connection."
}, },
"title": "Inspector", "title": "Помощник",
"url": { "url": {
"extension_not_installed": "Extension not installed.", "extension_not_installed": "Расширение не установлено.",
"extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", "extension_unknown_origin": "Убедитесь, что текущий домен добавлен в список доверенных ресурсов в расширении браузера",
"extention_enable_action": "Enable Browser Extension", "extention_enable_action": "Подключить расширение",
"extention_not_enabled": "Extension not enabled." "extention_not_enabled": "Расширение в браузере не подключено."
} }
}, },
"layout": { "layout": {
@@ -445,11 +481,11 @@
"modal": { "modal": {
"close_unsaved_tab": "У вас есть не сохранённые изменения", "close_unsaved_tab": "У вас есть не сохранённые изменения",
"collections": "Коллекции", "collections": "Коллекции",
"confirm": "Подтверждать", "confirm": "Подтвердите действие",
"customize_request": "Customize Request", "customize_request": "Customize Request",
"edit_request": "Изменить запрос", "edit_request": "Изменить запрос",
"import_export": "Импорт Экспорт", "import_export": "Импорт Экспорт",
"share_request": "Share Request" "share_request": "Поделиться запросом"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "Вы уже подписаны на этот топик", "already_subscribed": "Вы уже подписаны на этот топик",
@@ -485,7 +521,7 @@
"doc": "Документы", "doc": "Документы",
"graphql": "GraphQL", "graphql": "GraphQL",
"profile": "Профиль", "profile": "Профиль",
"realtime": "В реальном времени", "realtime": "Realtime",
"rest": "REST", "rest": "REST",
"settings": "Настройки" "settings": "Настройки"
}, },
@@ -507,8 +543,8 @@
"roles": "Роли", "roles": "Роли",
"roles_description": "Роли позволяют настраивать доступ конкретным людям к публичным коллекциям.", "roles_description": "Роли позволяют настраивать доступ конкретным людям к публичным коллекциям.",
"updated": "Профиль обновлен", "updated": "Профиль обновлен",
"viewer": "Зритель", "viewer": "Читатель",
"viewer_description": "Зрительно могут только просматривать и использовать запросы." "viewer_description": "Могут только просматривать и использовать запросы."
}, },
"remove": { "remove": {
"star": "Удалить звезду" "star": "Удалить звезду"
@@ -530,11 +566,11 @@
"enter_curl": "Введите сюда команду cURL", "enter_curl": "Введите сюда команду cURL",
"generate_code": "Сгенерировать код", "generate_code": "Сгенерировать код",
"generated_code": "Сгенерированный код", "generated_code": "Сгенерированный код",
"go_to_authorization_tab": "Go to Authorization", "go_to_authorization_tab": "Перейти на вкладку авторизации",
"go_to_body_tab": "Go to Body tab", "go_to_body_tab": "Перейти на вкладку тела запроса",
"header_list": "Список заголовков", "header_list": "Список заголовков",
"invalid_name": "Укажите имя для запроса", "invalid_name": "Укажите имя для запроса",
"method": "Методика", "method": "Метод",
"moved": "Запрос перемещён", "moved": "Запрос перемещён",
"name": "Имя запроса", "name": "Имя запроса",
"new": "Новый запрос", "new": "Новый запрос",
@@ -548,22 +584,22 @@
"payload": "Полезная нагрузка", "payload": "Полезная нагрузка",
"query": "Запрос", "query": "Запрос",
"raw_body": "Необработанное тело запроса", "raw_body": "Необработанное тело запроса",
"rename": "Переименость запрос", "rename": "Переименовать запрос",
"renamed": "Запрос переименован", "renamed": "Запрос переименован",
"request_variables": "Переменные запроса",
"run": "Запустить", "run": "Запустить",
"save": "Сохранить", "save": "Сохранить",
"save_as": "Сохранить как", "save_as": "Сохранить как",
"saved": "Запрос сохранен", "saved": "Запрос сохранен",
"share": "Делиться", "share": "Поделиться",
"share_description": "Поделиться Hoppscotch с друзьями", "share_description": "Поделиться Hoppscotch с друзьями",
"share_request": "Share Request", "share_request": "Поделиться запросом",
"stop": "Stop", "stop": "Стоп",
"title": "Запрос", "title": "Запрос",
"type": "Тип запроса", "type": "Тип запроса",
"url": "URL", "url": "URL",
"variables": "Переменные", "variables": "Переменные",
"view_my_links": "Посмотреть мои ссылки", "view_my_links": "Посмотреть мои ссылки"
"copy_link": "Копировать ссылку"
}, },
"response": { "response": {
"audio": "Аудио", "audio": "Аудио",
@@ -586,7 +622,7 @@
}, },
"settings": { "settings": {
"accent_color": "Основной цвет", "accent_color": "Основной цвет",
"account": "Счет", "account": "Аккаунт",
"account_deleted": "Ваш аккаунт был удалён", "account_deleted": "Ваш аккаунт был удалён",
"account_description": "Настройте параметры своей учетной записи.", "account_description": "Настройте параметры своей учетной записи.",
"account_email_description": "Ваш основной адрес электронной почты.", "account_email_description": "Ваш основной адрес электронной почты.",
@@ -639,29 +675,29 @@
"verify_email": "Подтвердить Email" "verify_email": "Подтвердить Email"
}, },
"shared_requests": { "shared_requests": {
"button": "Button", "button": "Кнопка",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "button_info": "Создать кнопку 'Run in Hoppscotch' на свой сайт, блог или README.",
"copy_html": "Copy HTML", "copy_html": "Копировать HTML код",
"copy_link": "Copy Link", "copy_link": "Копировать ссылку",
"copy_markdown": "Copy Markdown", "copy_markdown": "Копировать Markdown",
"creating_widget": "Creating widget", "creating_widget": "Создание виджет",
"customize": "Customize", "customize": "Настроить",
"deleted": "Shared request deleted", "deleted": "Запрос удален",
"description": "Select a widget, you can change and customize this later", "description": "Выберите вид как вы поделитесь запросом, позже вы сможете дополнительно его настроить",
"embed": "Embed", "embed": "Встраиваемое окно",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", "embed_info": "Добавьте небольшую площадку 'Hoppscotch API Playground' на свой веб-сайт, блог или документацию.",
"link": "Link", "link": "Ссылка",
"link_info": "Create a shareable link to share with anyone on the internet with view access.", "link_info": "Создайте общедоступную ссылку, которой можно поделиться с любым пользователем, имеющим доступ к просмотру.",
"modified": "Shared request modified", "modified": "Запрос изменен",
"not_found": "Shared request not found", "not_found": "Такой ссылке не нашлось",
"open_new_tab": "Open in new tab", "open_new_tab": "Открыть в новом окне",
"preview": "Preview", "preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch", "run_in_hoppscotch": "Run in Hoppscotch",
"theme": { "theme": {
"dark": "Dark", "dark": "Темная",
"light": "Light", "light": "Светлая",
"system": "System", "system": "Системная",
"title": "Theme" "title": "Тема"
} }
}, },
"shortcut": { "shortcut": {
@@ -669,7 +705,7 @@
"close_current_menu": "Закрыть текущее меню", "close_current_menu": "Закрыть текущее меню",
"command_menu": "Меню поиска и команд", "command_menu": "Меню поиска и команд",
"help_menu": "Меню помощи", "help_menu": "Меню помощи",
"show_all": "Горячие клавиши", "show_all": "Список горячих клавиш",
"title": "Общий" "title": "Общий"
}, },
"miscellaneous": { "miscellaneous": {
@@ -696,20 +732,19 @@
"get_method": "Выберите метод GET", "get_method": "Выберите метод GET",
"head_method": "Выберите метод HEAD", "head_method": "Выберите метод HEAD",
"import_curl": "Импортировать из cURL", "import_curl": "Импортировать из cURL",
"method": "Методика", "method": "Метод",
"next_method": "Выберите следующий метод", "next_method": "Выберите следующий метод",
"post_method": "Выберите метод POST", "post_method": "Выберите метод POST",
"previous_method": "Выбрать предыдущий метод", "previous_method": "Выбрать предыдущий метод",
"put_method": "Выберите метод PUT", "put_method": "Выберите метод PUT",
"rename": "Переименовать запрос", "rename": "Переименовать запрос",
"reset_request": "Сбросить запрос", "reset_request": "Сбросить запрос",
"save_request": "Сохарнить запрос", "save_request": "Сохранить запрос",
"save_to_collections": "Сохранить в коллекции", "save_to_collections": "Сохранить в коллекции",
"send_request": "Послать запрос", "send_request": "Послать запрос",
"share_request": "Share Request", "share_request": "Поделиться запросом",
"show_code": "Generate code snippet", "show_code": "Сгенерировать фрагмент кода из запроса",
"title": "Запрос", "title": "Запрос"
"copy_request_link": "Копировать ссылку на запрос"
}, },
"response": { "response": {
"copy": "Копировать запрос в буфер обмена", "copy": "Копировать запрос в буфер обмена",
@@ -717,11 +752,11 @@
"title": "Запрос" "title": "Запрос"
}, },
"theme": { "theme": {
"black": "Черный режим", "black": "Переключить на черный режим",
"dark": "Тёмный режим", "dark": "Переключить на тёмный режим",
"light": "Светлый режим", "light": "Переключить на светлый режим",
"system": "Определяется системой", "system": "Переключить на тему, исходя из настроек системы",
"title": "Тема" "title": "Внешний вид"
} }
}, },
"show": { "show": {
@@ -730,6 +765,11 @@
"more": "Показать больше", "more": "Показать больше",
"sidebar": "Показать боковую панель" "sidebar": "Показать боковую панель"
}, },
"site_protection": {
"error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status",
"login_to_continue": "Login to continue",
"login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance."
},
"socketio": { "socketio": {
"communication": "Коммуникация", "communication": "Коммуникация",
"connection_not_authorized": "Это SocketIO соединение не использует какую-либо авторизацию.", "connection_not_authorized": "Это SocketIO соединение не использует какую-либо авторизацию.",
@@ -739,82 +779,89 @@
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "Change Language", "change_language": "Изменить язык",
"environments": { "environments": {
"delete": "Delete current environment", "delete": "Удалить текущее окружение",
"duplicate": "Duplicate current environment", "duplicate": "Дублировать текущее окружение",
"duplicate_global": "Duplicate global environment", "duplicate_global": "Дублировать глобальное окружение",
"edit": "Edit current environment", "edit": "Редактировать текущее окружение",
"edit_global": "Edit global environment", "edit_global": "Редактировать глобальное окружение",
"new": "Create new environment", "new": "Создать новое окружение",
"new_variable": "Create a new environment variable", "new_variable": "Создать новую переменную окружения",
"title": "Environments" "title": "Окружение"
}, },
"general": { "general": {
"chat": "Chat with support", "chat": "Чат с поддержкой",
"help_menu": "Help and support", "help_menu": "Помощь",
"open_docs": "Read Documentation", "open_docs": "Почитать документацию",
"open_github": "Open GitHub repository", "open_github": "Открыть GitHub репозиторий",
"open_keybindings": "Keyboard shortcuts", "open_keybindings": "Горячие клавиши",
"social": "Social", "social": "Соц. сети",
"title": "General" "title": "Общее"
}, },
"graphql": { "graphql": {
"connect": "Connect to server", "connect": "Подключиться к серверу",
"disconnect": "Disconnect from server" "disconnect": "Отключиться от сервера"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "Invite your friends to Hoppscotch", "invite": "Пригласить друзей в Hoppscotch",
"title": "Miscellaneous" "title": "Другое"
},
"phrases": {
"create_environment": "Создать окружение",
"create_workspace": "Создать пространство",
"import_collections": "Импортировать коллекцию",
"share_request": "Поделиться запросом",
"try": "Попробовать"
}, },
"request": { "request": {
"save_as_new": "Save as new request", "save_as_new": "Сохранить как новый запрос",
"select_method": "Select method", "select_method": "Выбрать метод",
"switch_to": "Switch to", "switch_to": "Переключиться",
"tab_authorization": "Authorization tab", "tab_authorization": "На вкладку авторизации",
"tab_body": "Body tab", "tab_body": "На вкладку тела запроса",
"tab_headers": "Headers tab", "tab_headers": "На вкладку заголовков",
"tab_parameters": "Parameters tab", "tab_parameters": "На вкладку параметров",
"tab_pre_request_script": "Pre-request script tab", "tab_pre_request_script": "На вкладку пред-скрипта запроса",
"tab_query": "Query tab", "tab_query": "На вкладку запроса",
"tab_tests": "Tests tab", "tab_tests": "На вкладку тестов",
"tab_variables": "Variables tab" "tab_variables": "На вкладку переменных запроса"
}, },
"response": { "response": {
"copy": "Copy response", "copy": "Копировать содержимое ответа",
"download": "Download response as file", "download": "Сказать содержимое ответа как файл",
"title": "Response" "title": "Ответ запроса"
}, },
"section": { "section": {
"interceptor": "Interceptor", "interceptor": "Перехватчик",
"interface": "Interface", "interface": "Интерфейс",
"theme": "Theme", "theme": "Внешний вид",
"user": "User" "user": "Пользователь"
}, },
"settings": { "settings": {
"change_interceptor": "Change Interceptor", "change_interceptor": "Изменить перехватчик",
"change_language": "Change Language", "change_language": "Изменить язык",
"theme": { "theme": {
"black": "Black", "black": "Черная",
"dark": "Dark", "dark": "Темная",
"light": "Light", "light": "Светлая",
"system": "System preference" "system": "Как задано в системе"
} }
}, },
"tab": { "tab": {
"close_current": "Close current tab", "close_current": "Закрыть текущую вкладку",
"close_others": "Close all other tabs", "close_others": "Закрыть все вкладки",
"duplicate": "Duplicate current tab", "duplicate": "Продублировать текущую вкладку",
"new_tab": "Open a new tab", "new_tab": "Открыть в новой вкладке",
"title": "Tabs" "title": "Вкладки"
}, },
"workspace": { "workspace": {
"delete": "Delete current team", "delete": "Удалить текущую команду",
"edit": "Edit current team", "edit": "Редактировать текущую команду",
"invite": "Invite people to team", "invite": "Пригласить людей в команду",
"new": "Create new team", "new": "Создать новую команду",
"switch_to_personal": "Switch to your personal workspace", "switch_to_personal": "Переключить на персональное пространство",
"title": "Teams" "title": "Команды"
} }
}, },
"sse": { "sse": {
@@ -824,7 +871,7 @@
}, },
"state": { "state": {
"bulk_mode": "Множественное редактирование", "bulk_mode": "Множественное редактирование",
"bulk_mode_placeholder": "Каждый параметр должен начинаться с новой строки\nКлючи и значения разедляются двоеточием\nИспользуйте # для комментария", "bulk_mode_placeholder": "Каждый параметр должен начинаться с новой строки\nКлючи и значения разделяются двоеточием\nИспользуйте # для комментария",
"cleared": "Очищено", "cleared": "Очищено",
"connected": "Связаны", "connected": "Связаны",
"connected_to": "Подключено к {name}", "connected_to": "Подключено к {name}",
@@ -843,20 +890,20 @@
"download_failed": "Download failed", "download_failed": "Download failed",
"download_started": "Скачивание началось", "download_started": "Скачивание началось",
"enabled": "Включено", "enabled": "Включено",
"file_imported": "Файл импортирован", "file_imported": "Файл успешно импортирован",
"finished_in": "Завершено через {duration} мс", "finished_in": "Завершено через {duration} мс",
"hide": "Hide", "hide": "Скрыть",
"history_deleted": "История удалена", "history_deleted": "История удалена",
"linewrap": "Обернуть линии", "linewrap": "Обернуть линии",
"loading": "Загрузка...", "loading": "Загрузка...",
"message_received": "Сообщение: {message} получено по топику: {topic}", "message_received": "Сообщение: {message} получено по топику: {topic}",
"mqtt_subscription_failed": "Что-то пошло не так, при попытке подписаться на топик: {topic}", "mqtt_subscription_failed": "Что-то пошло не так, при попытке подписаться на топик: {topic}",
"none": "Никто", "none": "Не задан",
"nothing_found": "Ничего не найдено для", "nothing_found": "Ничего не найдено для",
"published_error": "Что-то пошло не так при попытке опубликовать сообщение в топик {topic}: {message}", "published_error": "Что-то пошло не так при попытке опубликовать сообщение в топик {topic}: {message}",
"published_message": "Опубликовано сообщение: {message} в топик: {topic}", "published_message": "Опубликовано сообщение: {message} в топик: {topic}",
"reconnection_error": "Не удалось переподключиться", "reconnection_error": "Не удалось переподключиться",
"show": "Show", "show": "Показать",
"subscribed_failed": "Не удалось подписаться на топик: {topic}", "subscribed_failed": "Не удалось подписаться на топик: {topic}",
"subscribed_success": "Успешно подписался на топик: {topic}", "subscribed_success": "Успешно подписался на топик: {topic}",
"unsubscribed_failed": "Не удалось отписаться от топика: {topic}", "unsubscribed_failed": "Не удалось отписаться от топика: {topic}",
@@ -871,7 +918,6 @@
"forum": "Задавайте вопросы и получайте ответы", "forum": "Задавайте вопросы и получайте ответы",
"github": "Подпишитесь на нас на Github", "github": "Подпишитесь на нас на Github",
"shortcuts": "Просматривайте приложение быстрее", "shortcuts": "Просматривайте приложение быстрее",
"team": "Свяжитесь с командой",
"title": "Служба поддержки", "title": "Служба поддержки",
"twitter": "Следуйте за нами на Twitter" "twitter": "Следуйте за нами на Twitter"
}, },
@@ -882,7 +928,7 @@
"close_others": "Закрыть остальные вкладки", "close_others": "Закрыть остальные вкладки",
"collections": "Коллекции", "collections": "Коллекции",
"documentation": "Документация", "documentation": "Документация",
"duplicate": "Duplicate Tab", "duplicate": "Дублировать вкладку",
"environments": "Окружения", "environments": "Окружения",
"headers": "Заголовки", "headers": "Заголовки",
"history": "История", "history": "История",
@@ -892,7 +938,8 @@
"queries": "Запросы", "queries": "Запросы",
"query": "Запрос", "query": "Запрос",
"schema": "Схема", "schema": "Схема",
"shared_requests": "Shared Requests", "shared_requests": "Запросы в общем доступе",
"share_tab_request": "Поделиться запросом",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Тесты", "tests": "Тесты",
@@ -921,7 +968,6 @@
"invite_tooltip": "Пригласить людей в Ваше рабочее пространство", "invite_tooltip": "Пригласить людей в Ваше рабочее пространство",
"invited_to_team": "{owner} приглашает Вас присоединиться к команде {team}", "invited_to_team": "{owner} приглашает Вас присоединиться к команде {team}",
"join": "Приглашение принято", "join": "Приглашение принято",
"join_beta": "Присоединяйтесь к бета-программе, чтобы получить доступ к командам.",
"join_team": "Присоединиться к {team}", "join_team": "Присоединиться к {team}",
"joined_team": "Вы присоединились к команде {team}", "joined_team": "Вы присоединились к команде {team}",
"joined_team_description": "Теперь Вы участник этой команды", "joined_team_description": "Теперь Вы участник этой команды",
@@ -950,6 +996,7 @@
"permissions": "Разрешения", "permissions": "Разрешения",
"same_target_destination": "Таже цель и конечная точка", "same_target_destination": "Таже цель и конечная точка",
"saved": "Команда сохранена", "saved": "Команда сохранена",
"search_title": "Team Requests",
"select_a_team": "Выбрать команду", "select_a_team": "Выбрать команду",
"success_invites": "Принятые приглашения", "success_invites": "Принятые приглашения",
"title": "Команды", "title": "Команды",
@@ -981,16 +1028,8 @@
"workspace": { "workspace": {
"change": "Изменить пространство", "change": "Изменить пространство",
"personal": "Моё пространство", "personal": "Моё пространство",
"other_workspaces": "Пространства",
"team": "Пространство команды", "team": "Пространство команды",
"title": "Рабочие пространства" "title": "Рабочие пространства"
},
"shortcodes": {
"actions": "Действия",
"created_on": "Создано",
"deleted": "Удалёна",
"method": "Метод",
"not_found": "Короткая ссылка не найдена",
"short_code": "Короткая ссылка",
"url": "URL"
} }
} }

View File

@@ -68,9 +68,9 @@
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
"discord": "Discord", "discord": "Discord",
"documentation": "Dökümanlar", "documentation": "Dokümanlar",
"github": "GitHub", "github": "GitHub",
"help": "Yardım, geri bildirim ve dökümanlar", "help": "Yardım, geri bildirim ve dokümanlar",
"home": "Ana sayfa", "home": "Ana sayfa",
"invite": "Davet et", "invite": "Davet et",
"invite_description": "Hoppscotch'ta API'lerinizi oluşturmak ve yönetmek için basit ve sezgisel bir arayüz tasarladık. Hoppscotch, API'lerinizi oluşturmanıza, test etmenize, belgelemenize ve paylaşmanıza yardımcı olan bir araçtır.", "invite_description": "Hoppscotch'ta API'lerinizi oluşturmak ve yönetmek için basit ve sezgisel bir arayüz tasarladık. Hoppscotch, API'lerinizi oluşturmanıza, test etmenize, belgelemenize ve paylaşmanıza yardımcı olan bir araçtır.",
@@ -225,7 +225,7 @@
"body": "Bu isteğin bir gövdesi yok", "body": "Bu isteğin bir gövdesi yok",
"collection": "Koleksiyon boş", "collection": "Koleksiyon boş",
"collections": "Koleksiyonlar boş", "collections": "Koleksiyonlar boş",
"documentation": "Dökümanları görmek için GraphQL uç noktasını bağlayın", "documentation": "Dokümanları görmek için GraphQL uç noktasını bağlayın",
"endpoint": "Uç nokta boş olamaz", "endpoint": "Uç nokta boş olamaz",
"environments": "Ortamlar boş", "environments": "Ortamlar boş",
"folder": "Klasör boş", "folder": "Klasör boş",
@@ -735,7 +735,7 @@
"url": "Bağlantı" "url": "Bağlantı"
}, },
"spotlight": { "spotlight": {
"change_language": "Change Language", "change_language": "Dil Değiştir",
"environments": { "environments": {
"delete": "Delete current environment", "delete": "Delete current environment",
"duplicate": "Duplicate current environment", "duplicate": "Duplicate current environment",
@@ -744,7 +744,7 @@
"edit_global": "Edit global environment", "edit_global": "Edit global environment",
"new": "Create new environment", "new": "Create new environment",
"new_variable": "Create a new environment variable", "new_variable": "Create a new environment variable",
"title": "Environments" "title": "Ortamlar"
}, },
"general": { "general": {
"chat": "Chat with support", "chat": "Chat with support",

View File

@@ -1,7 +1,7 @@
{ {
"name": "@hoppscotch/common", "name": "@hoppscotch/common",
"private": true, "private": true,
"version": "2024.3.0", "version": "2024.3.3",
"scripts": { "scripts": {
"dev": "pnpm exec npm-run-all -p -l dev:*", "dev": "pnpm exec npm-run-all -p -l dev:*",
"test": "vitest --run", "test": "vitest --run",
@@ -50,7 +50,7 @@
"axios": "1.6.2", "axios": "1.6.2",
"buffer": "6.0.3", "buffer": "6.0.3",
"cookie-es": "1.0.0", "cookie-es": "1.0.0",
"dioc": "1.0.1", "dioc": "3.0.1",
"esprima": "4.0.1", "esprima": "4.0.1",
"events": "3.3.0", "events": "3.3.0",
"fp-ts": "2.16.1", "fp-ts": "2.16.1",
@@ -90,7 +90,7 @@
"util": "0.12.5", "util": "0.12.5",
"uuid": "9.0.1", "uuid": "9.0.1",
"verzod": "0.2.2", "verzod": "0.2.2",
"vue": "3.3.9", "vue": "3.4.27",
"vue-i18n": "9.8.0", "vue-i18n": "9.8.0",
"vue-pdf-embed": "1.2.1", "vue-pdf-embed": "1.2.1",
"vue-router": "4.2.5", "vue-router": "4.2.5",

View File

@@ -3,214 +3,221 @@
// @ts-nocheck // @ts-nocheck
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
import "@vue/runtime-core"
export {} export {}
declare module "@vue/runtime-core" { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AppActionHandler: (typeof import("./components/app/ActionHandler.vue"))["default"] AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default']
AppBanner: (typeof import("./components/app/Banner.vue"))["default"] AppBanner: typeof import('./components/app/Banner.vue')['default']
AppContextMenu: (typeof import("./components/app/ContextMenu.vue"))["default"] AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default']
AppDeveloperOptions: (typeof import("./components/app/DeveloperOptions.vue"))["default"] AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
AppFooter: (typeof import("./components/app/Footer.vue"))["default"] AppFooter: typeof import('./components/app/Footer.vue')['default']
AppGitHubStarButton: (typeof import("./components/app/GitHubStarButton.vue"))["default"] AppGitHubStarButton: typeof import('./components/app/GitHubStarButton.vue')['default']
AppHeader: (typeof import("./components/app/Header.vue"))["default"] AppHeader: typeof import('./components/app/Header.vue')['default']
AppInspection: (typeof import("./components/app/Inspection.vue"))["default"] AppInspection: typeof import('./components/app/Inspection.vue')['default']
AppInterceptor: (typeof import("./components/app/Interceptor.vue"))["default"] AppInterceptor: typeof import('./components/app/Interceptor.vue')['default']
AppLogo: (typeof import("./components/app/Logo.vue"))["default"] AppLogo: typeof import('./components/app/Logo.vue')['default']
AppOptions: (typeof import("./components/app/Options.vue"))["default"] AppOptions: typeof import('./components/app/Options.vue')['default']
AppPaneLayout: (typeof import("./components/app/PaneLayout.vue"))["default"] AppPaneLayout: typeof import('./components/app/PaneLayout.vue')['default']
AppShare: (typeof import("./components/app/Share.vue"))["default"] AppShare: typeof import('./components/app/Share.vue')['default']
AppShortcuts: (typeof import("./components/app/Shortcuts.vue"))["default"] AppShortcuts: typeof import('./components/app/Shortcuts.vue')['default']
AppShortcutsEntry: (typeof import("./components/app/ShortcutsEntry.vue"))["default"] AppShortcutsEntry: typeof import('./components/app/ShortcutsEntry.vue')['default']
AppShortcutsPrompt: (typeof import("./components/app/ShortcutsPrompt.vue"))["default"] AppShortcutsPrompt: typeof import('./components/app/ShortcutsPrompt.vue')['default']
AppSidenav: (typeof import("./components/app/Sidenav.vue"))["default"] AppSidenav: typeof import('./components/app/Sidenav.vue')['default']
AppSpotlight: (typeof import("./components/app/spotlight/index.vue"))["default"] AppSpotlight: typeof import('./components/app/spotlight/index.vue')['default']
AppSpotlightEntry: (typeof import("./components/app/spotlight/Entry.vue"))["default"] AppSpotlightEntry: typeof import('./components/app/spotlight/Entry.vue')['default']
AppSpotlightEntryGQLHistory: (typeof import("./components/app/spotlight/entry/GQLHistory.vue"))["default"] AppSpotlightEntryGQLHistory: typeof import('./components/app/spotlight/entry/GQLHistory.vue')['default']
AppSpotlightEntryGQLRequest: (typeof import("./components/app/spotlight/entry/GQLRequest.vue"))["default"] AppSpotlightEntryGQLRequest: typeof import('./components/app/spotlight/entry/GQLRequest.vue')['default']
AppSpotlightEntryIconSelected: (typeof import("./components/app/spotlight/entry/IconSelected.vue"))["default"] AppSpotlightEntryIconSelected: typeof import('./components/app/spotlight/entry/IconSelected.vue')['default']
AppSpotlightEntryRESTHistory: (typeof import("./components/app/spotlight/entry/RESTHistory.vue"))["default"] AppSpotlightEntryRESTHistory: typeof import('./components/app/spotlight/entry/RESTHistory.vue')['default']
AppSpotlightEntryRESTRequest: (typeof import("./components/app/spotlight/entry/RESTRequest.vue"))["default"] AppSpotlightEntryRESTRequest: typeof import('./components/app/spotlight/entry/RESTRequest.vue')['default']
AppSupport: (typeof import("./components/app/Support.vue"))["default"] AppSpotlightEntryRESTTeamRequestEntry: typeof import('./components/app/spotlight/entry/RESTTeamRequestEntry.vue')['default']
Collections: (typeof import("./components/collections/index.vue"))["default"] AppSpotlightSearch: typeof import('./components/app/SpotlightSearch.vue')['default']
CollectionsAdd: (typeof import("./components/collections/Add.vue"))["default"] AppSupport: typeof import('./components/app/Support.vue')['default']
CollectionsAddFolder: (typeof import("./components/collections/AddFolder.vue"))["default"] Collections: typeof import('./components/collections/index.vue')['default']
CollectionsAddRequest: (typeof import("./components/collections/AddRequest.vue"))["default"] CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
CollectionsCollection: (typeof import("./components/collections/Collection.vue"))["default"] CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
CollectionsEdit: (typeof import("./components/collections/Edit.vue"))["default"] CollectionsAddRequest: typeof import('./components/collections/AddRequest.vue')['default']
CollectionsEditFolder: (typeof import("./components/collections/EditFolder.vue"))["default"] CollectionsCollection: typeof import('./components/collections/Collection.vue')['default']
CollectionsEditRequest: (typeof import("./components/collections/EditRequest.vue"))["default"] CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
CollectionsGraphql: (typeof import("./components/collections/graphql/index.vue"))["default"] CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
CollectionsGraphqlAdd: (typeof import("./components/collections/graphql/Add.vue"))["default"] CollectionsEditRequest: typeof import('./components/collections/EditRequest.vue')['default']
CollectionsGraphqlAddFolder: (typeof import("./components/collections/graphql/AddFolder.vue"))["default"] CollectionsGraphql: typeof import('./components/collections/graphql/index.vue')['default']
CollectionsGraphqlAddRequest: (typeof import("./components/collections/graphql/AddRequest.vue"))["default"] CollectionsGraphqlAdd: typeof import('./components/collections/graphql/Add.vue')['default']
CollectionsGraphqlCollection: (typeof import("./components/collections/graphql/Collection.vue"))["default"] CollectionsGraphqlAddFolder: typeof import('./components/collections/graphql/AddFolder.vue')['default']
CollectionsGraphqlEdit: (typeof import("./components/collections/graphql/Edit.vue"))["default"] CollectionsGraphqlAddRequest: typeof import('./components/collections/graphql/AddRequest.vue')['default']
CollectionsGraphqlEditFolder: (typeof import("./components/collections/graphql/EditFolder.vue"))["default"] CollectionsGraphqlCollection: typeof import('./components/collections/graphql/Collection.vue')['default']
CollectionsGraphqlEditRequest: (typeof import("./components/collections/graphql/EditRequest.vue"))["default"] CollectionsGraphqlEdit: typeof import('./components/collections/graphql/Edit.vue')['default']
CollectionsGraphqlFolder: (typeof import("./components/collections/graphql/Folder.vue"))["default"] CollectionsGraphqlEditFolder: typeof import('./components/collections/graphql/EditFolder.vue')['default']
CollectionsGraphqlImportExport: (typeof import("./components/collections/graphql/ImportExport.vue"))["default"] CollectionsGraphqlEditRequest: typeof import('./components/collections/graphql/EditRequest.vue')['default']
CollectionsGraphqlRequest: (typeof import("./components/collections/graphql/Request.vue"))["default"] CollectionsGraphqlFolder: typeof import('./components/collections/graphql/Folder.vue')['default']
CollectionsImportExport: (typeof import("./components/collections/ImportExport.vue"))["default"] CollectionsGraphqlImportExport: typeof import('./components/collections/graphql/ImportExport.vue')['default']
CollectionsMyCollections: (typeof import("./components/collections/MyCollections.vue"))["default"] CollectionsGraphqlRequest: typeof import('./components/collections/graphql/Request.vue')['default']
CollectionsProperties: (typeof import("./components/collections/Properties.vue"))["default"] CollectionsImportExport: typeof import('./components/collections/ImportExport.vue')['default']
CollectionsRequest: (typeof import("./components/collections/Request.vue"))["default"] CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default']
CollectionsSaveRequest: (typeof import("./components/collections/SaveRequest.vue"))["default"] CollectionsProperties: typeof import('./components/collections/Properties.vue')['default']
CollectionsTeamCollections: (typeof import("./components/collections/TeamCollections.vue"))["default"] CollectionsRequest: typeof import('./components/collections/Request.vue')['default']
CookiesAllModal: (typeof import("./components/cookies/AllModal.vue"))["default"] CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default']
CookiesEditCookie: (typeof import("./components/cookies/EditCookie.vue"))["default"] CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default']
Embeds: (typeof import("./components/embeds/index.vue"))["default"] CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default']
Environments: (typeof import("./components/environments/index.vue"))["default"] CookiesEditCookie: typeof import('./components/cookies/EditCookie.vue')['default']
EnvironmentsAdd: (typeof import("./components/environments/Add.vue"))["default"] Embeds: typeof import('./components/embeds/index.vue')['default']
EnvironmentsImportExport: (typeof import("./components/environments/ImportExport.vue"))["default"] Environments: typeof import('./components/environments/index.vue')['default']
EnvironmentsMy: (typeof import("./components/environments/my/index.vue"))["default"] EnvironmentsAdd: typeof import('./components/environments/Add.vue')['default']
EnvironmentsMyDetails: (typeof import("./components/environments/my/Details.vue"))["default"] EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default']
EnvironmentsMyEnvironment: (typeof import("./components/environments/my/Environment.vue"))["default"] EnvironmentsMy: typeof import('./components/environments/my/index.vue')['default']
EnvironmentsSelector: (typeof import("./components/environments/Selector.vue"))["default"] EnvironmentsMyDetails: typeof import('./components/environments/my/Details.vue')['default']
EnvironmentsTeams: (typeof import("./components/environments/teams/index.vue"))["default"] EnvironmentsMyEnvironment: typeof import('./components/environments/my/Environment.vue')['default']
EnvironmentsTeamsDetails: (typeof import("./components/environments/teams/Details.vue"))["default"] EnvironmentsSelector: typeof import('./components/environments/Selector.vue')['default']
EnvironmentsTeamsEnvironment: (typeof import("./components/environments/teams/Environment.vue"))["default"] EnvironmentsTeams: typeof import('./components/environments/teams/index.vue')['default']
FirebaseLogin: (typeof import("./components/firebase/Login.vue"))["default"] EnvironmentsTeamsDetails: typeof import('./components/environments/teams/Details.vue')['default']
FirebaseLogout: (typeof import("./components/firebase/Logout.vue"))["default"] EnvironmentsTeamsEnvironment: typeof import('./components/environments/teams/Environment.vue')['default']
GraphqlAuthorization: (typeof import("./components/graphql/Authorization.vue"))["default"] FirebaseLogin: typeof import('./components/firebase/Login.vue')['default']
GraphqlField: (typeof import("./components/graphql/Field.vue"))["default"] FirebaseLogout: typeof import('./components/firebase/Logout.vue')['default']
GraphqlHeaders: (typeof import("./components/graphql/Headers.vue"))["default"] GraphqlAuthorization: typeof import('./components/graphql/Authorization.vue')['default']
GraphqlQuery: (typeof import("./components/graphql/Query.vue"))["default"] GraphqlField: typeof import('./components/graphql/Field.vue')['default']
GraphqlRequest: (typeof import("./components/graphql/Request.vue"))["default"] GraphqlHeaders: typeof import('./components/graphql/Headers.vue')['default']
GraphqlRequestOptions: (typeof import("./components/graphql/RequestOptions.vue"))["default"] GraphqlQuery: typeof import('./components/graphql/Query.vue')['default']
GraphqlRequestTab: (typeof import("./components/graphql/RequestTab.vue"))["default"] GraphqlRequest: typeof import('./components/graphql/Request.vue')['default']
GraphqlResponse: (typeof import("./components/graphql/Response.vue"))["default"] GraphqlRequestOptions: typeof import('./components/graphql/RequestOptions.vue')['default']
GraphqlSidebar: (typeof import("./components/graphql/Sidebar.vue"))["default"] GraphqlRequestTab: typeof import('./components/graphql/RequestTab.vue')['default']
GraphqlSubscriptionLog: (typeof import("./components/graphql/SubscriptionLog.vue"))["default"] GraphqlResponse: typeof import('./components/graphql/Response.vue')['default']
GraphqlTabHead: (typeof import("./components/graphql/TabHead.vue"))["default"] GraphqlSidebar: typeof import('./components/graphql/Sidebar.vue')['default']
GraphqlType: (typeof import("./components/graphql/Type.vue"))["default"] GraphqlSubscriptionLog: typeof import('./components/graphql/SubscriptionLog.vue')['default']
GraphqlTypeLink: (typeof import("./components/graphql/TypeLink.vue"))["default"] GraphqlTabHead: typeof import('./components/graphql/TabHead.vue')['default']
GraphqlVariable: (typeof import("./components/graphql/Variable.vue"))["default"] GraphqlType: typeof import('./components/graphql/Type.vue')['default']
History: (typeof import("./components/history/index.vue"))["default"] GraphqlTypeLink: typeof import('./components/graphql/TypeLink.vue')['default']
HistoryGraphqlCard: (typeof import("./components/history/graphql/Card.vue"))["default"] GraphqlVariable: typeof import('./components/graphql/Variable.vue')['default']
HistoryRestCard: (typeof import("./components/history/rest/Card.vue"))["default"] History: typeof import('./components/history/index.vue')['default']
HoppButtonPrimary: (typeof import("@hoppscotch/ui"))["HoppButtonPrimary"] HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default']
HoppButtonSecondary: (typeof import("@hoppscotch/ui"))["HoppButtonSecondary"] HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default']
HoppSmartAnchor: (typeof import("@hoppscotch/ui"))["HoppSmartAnchor"] HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppSmartCheckbox: (typeof import("@hoppscotch/ui"))["HoppSmartCheckbox"] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
HoppSmartConfirmModal: (typeof import("@hoppscotch/ui"))["HoppSmartConfirmModal"] HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
HoppSmartExpand: (typeof import("@hoppscotch/ui"))["HoppSmartExpand"] HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
HoppSmartFileChip: (typeof import("@hoppscotch/ui"))["HoppSmartFileChip"] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartInput: (typeof import("@hoppscotch/ui"))["HoppSmartInput"] HoppSmartExpand: typeof import('@hoppscotch/ui')['HoppSmartExpand']
HoppSmartIntersection: (typeof import("@hoppscotch/ui"))["HoppSmartIntersection"] HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
HoppSmartItem: (typeof import("@hoppscotch/ui"))["HoppSmartItem"] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartLink: (typeof import("@hoppscotch/ui"))["HoppSmartLink"] HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
HoppSmartModal: (typeof import("@hoppscotch/ui"))["HoppSmartModal"] HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
HoppSmartPicture: (typeof import("@hoppscotch/ui"))["HoppSmartPicture"] HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
HoppSmartPlaceholder: (typeof import("@hoppscotch/ui"))["HoppSmartPlaceholder"] HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartProgressRing: (typeof import("@hoppscotch/ui"))["HoppSmartProgressRing"] HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
HoppSmartRadio: (typeof import("@hoppscotch/ui"))["HoppSmartRadio"] HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']
HoppSmartRadioGroup: (typeof import("@hoppscotch/ui"))["HoppSmartRadioGroup"] HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing']
HoppSmartSelectWrapper: (typeof import("@hoppscotch/ui"))["HoppSmartSelectWrapper"] HoppSmartRadio: typeof import('@hoppscotch/ui')['HoppSmartRadio']
HoppSmartSlideOver: (typeof import("@hoppscotch/ui"))["HoppSmartSlideOver"] HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup']
HoppSmartSpinner: (typeof import("@hoppscotch/ui"))["HoppSmartSpinner"] HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper']
HoppSmartTab: (typeof import("@hoppscotch/ui"))["HoppSmartTab"] HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver']
HoppSmartTabs: (typeof import("@hoppscotch/ui"))["HoppSmartTabs"] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
HoppSmartToggle: (typeof import("@hoppscotch/ui"))["HoppSmartToggle"] HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
HoppSmartTree: (typeof import("@hoppscotch/ui"))["HoppSmartTree"] HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
HoppSmartWindow: (typeof import("@hoppscotch/ui"))["HoppSmartWindow"] HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
HoppSmartWindows: (typeof import("@hoppscotch/ui"))["HoppSmartWindows"] HoppSmartTree: typeof import('@hoppscotch/ui')['HoppSmartTree']
HttpAuthorization: (typeof import("./components/http/Authorization.vue"))["default"] HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow']
HttpAuthorizationApiKey: (typeof import("./components/http/authorization/ApiKey.vue"))["default"] HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows']
HttpAuthorizationBasic: (typeof import("./components/http/authorization/Basic.vue"))["default"] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default']
HttpBody: (typeof import("./components/http/Body.vue"))["default"] HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default']
HttpBodyParameters: (typeof import("./components/http/BodyParameters.vue"))["default"] HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default']
HttpCodegenModal: (typeof import("./components/http/CodegenModal.vue"))["default"] HttpBody: typeof import('./components/http/Body.vue')['default']
HttpHeaders: (typeof import("./components/http/Headers.vue"))["default"] HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default']
HttpImportCurl: (typeof import("./components/http/ImportCurl.vue"))["default"] HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default']
HttpOAuth2Authorization: (typeof import("./components/http/OAuth2Authorization.vue"))["default"] HttpHeaders: typeof import('./components/http/Headers.vue')['default']
HttpParameters: (typeof import("./components/http/Parameters.vue"))["default"] HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default']
HttpPreRequestScript: (typeof import("./components/http/PreRequestScript.vue"))["default"] HttpOAuth2Authorization: typeof import('./components/http/OAuth2Authorization.vue')['default']
HttpRawBody: (typeof import("./components/http/RawBody.vue"))["default"] HttpParameters: typeof import('./components/http/Parameters.vue')['default']
HttpReqChangeConfirmModal: (typeof import("./components/http/ReqChangeConfirmModal.vue"))["default"] HttpPreRequestScript: typeof import('./components/http/PreRequestScript.vue')['default']
HttpRequest: (typeof import("./components/http/Request.vue"))["default"] HttpRawBody: typeof import('./components/http/RawBody.vue')['default']
HttpRequestOptions: (typeof import("./components/http/RequestOptions.vue"))["default"] HttpReqChangeConfirmModal: typeof import('./components/http/ReqChangeConfirmModal.vue')['default']
HttpRequestTab: (typeof import("./components/http/RequestTab.vue"))["default"] HttpRequest: typeof import('./components/http/Request.vue')['default']
HttpResponse: (typeof import("./components/http/Response.vue"))["default"] HttpRequestOptions: typeof import('./components/http/RequestOptions.vue')['default']
HttpResponseMeta: (typeof import("./components/http/ResponseMeta.vue"))["default"] HttpRequestTab: typeof import('./components/http/RequestTab.vue')['default']
HttpSidebar: (typeof import("./components/http/Sidebar.vue"))["default"] HttpRequestVariables: typeof import('./components/http/RequestVariables.vue')['default']
HttpTabHead: (typeof import("./components/http/TabHead.vue"))["default"] HttpResponse: typeof import('./components/http/Response.vue')['default']
HttpTestResult: (typeof import("./components/http/TestResult.vue"))["default"] HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default']
HttpTestResultEntry: (typeof import("./components/http/TestResultEntry.vue"))["default"] HttpSidebar: typeof import('./components/http/Sidebar.vue')['default']
HttpTestResultEnv: (typeof import("./components/http/TestResultEnv.vue"))["default"] HttpTabHead: typeof import('./components/http/TabHead.vue')['default']
HttpTestResultReport: (typeof import("./components/http/TestResultReport.vue"))["default"] HttpTestResult: typeof import('./components/http/TestResult.vue')['default']
HttpTests: (typeof import("./components/http/Tests.vue"))["default"] HttpTestResultEntry: typeof import('./components/http/TestResultEntry.vue')['default']
HttpURLEncodedParams: (typeof import("./components/http/URLEncodedParams.vue"))["default"] HttpTestResultEnv: typeof import('./components/http/TestResultEnv.vue')['default']
IconLucideActivity: (typeof import("~icons/lucide/activity"))["default"] HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default']
IconLucideAlertTriangle: (typeof import("~icons/lucide/alert-triangle"))["default"] HttpTests: typeof import('./components/http/Tests.vue')['default']
IconLucideArrowLeft: (typeof import("~icons/lucide/arrow-left"))["default"] HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
IconLucideArrowUpRight: (typeof import("~icons/lucide/arrow-up-right"))["default"] IconLucideActivity: typeof import('~icons/lucide/activity')['default']
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
IconLucideBrush: (typeof import("~icons/lucide/brush"))["default"] IconLucideBrush: (typeof import("~icons/lucide/brush"))["default"]
IconLucideCheckCircle: (typeof import("~icons/lucide/check-circle"))["default"] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
IconLucideChevronRight: (typeof import("~icons/lucide/chevron-right"))["default"] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideGlobe: (typeof import("~icons/lucide/globe"))["default"] IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
IconLucideHelpCircle: (typeof import("~icons/lucide/help-circle"))["default"] IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
IconLucideInbox: (typeof import("~icons/lucide/inbox"))["default"] IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideInfo: (typeof import("~icons/lucide/info"))["default"] IconLucideInfo: typeof import('~icons/lucide/info')['default']
IconLucideLayers: (typeof import("~icons/lucide/layers"))["default"] IconLucideLayers: typeof import('~icons/lucide/layers')['default']
IconLucideListEnd: (typeof import("~icons/lucide/list-end"))["default"] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
IconLucideMinus: (typeof import("~icons/lucide/minus"))["default"] IconLucideMinus: typeof import('~icons/lucide/minus')['default']
IconLucideRss: (typeof import("~icons/lucide/rss"))["default"] IconLucideRss: (typeof import("~icons/lucide/rss"))["default"]
IconLucideSearch: (typeof import("~icons/lucide/search"))["default"] IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideUsers: (typeof import("~icons/lucide/users"))["default"] IconLucideUsers: typeof import('~icons/lucide/users')['default']
IconLucideX: (typeof import("~icons/lucide/x"))["default"] IconLucideX: typeof import('~icons/lucide/x')['default']
ImportExportBase: (typeof import("./components/importExport/Base.vue"))["default"] ImportExportBase: typeof import('./components/importExport/Base.vue')['default']
ImportExportImportExportList: (typeof import("./components/importExport/ImportExportList.vue"))["default"] ImportExportImportExportList: typeof import('./components/importExport/ImportExportList.vue')['default']
ImportExportImportExportSourcesList: (typeof import("./components/importExport/ImportExportSourcesList.vue"))["default"] ImportExportImportExportSourcesList: typeof import('./components/importExport/ImportExportSourcesList.vue')['default']
ImportExportImportExportStepsFileImport: (typeof import("./components/importExport/ImportExportSteps/FileImport.vue"))["default"] ImportExportImportExportStepsFileImport: typeof import('./components/importExport/ImportExportSteps/FileImport.vue')['default']
ImportExportImportExportStepsMyCollectionImport: (typeof import("./components/importExport/ImportExportSteps/MyCollectionImport.vue"))["default"] ImportExportImportExportStepsMyCollectionImport: typeof import('./components/importExport/ImportExportSteps/MyCollectionImport.vue')['default']
ImportExportImportExportStepsUrlImport: (typeof import("./components/importExport/ImportExportSteps/UrlImport.vue"))["default"] ImportExportImportExportStepsUrlImport: typeof import('./components/importExport/ImportExportSteps/UrlImport.vue')['default']
InterceptorsErrorPlaceholder: (typeof import("./components/interceptors/ErrorPlaceholder.vue"))["default"] InterceptorsErrorPlaceholder: typeof import('./components/interceptors/ErrorPlaceholder.vue')['default']
InterceptorsExtensionSubtitle: (typeof import("./components/interceptors/ExtensionSubtitle.vue"))["default"] InterceptorsExtensionSubtitle: typeof import('./components/interceptors/ExtensionSubtitle.vue')['default']
LensesHeadersRenderer: (typeof import("./components/lenses/HeadersRenderer.vue"))["default"] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default']
LensesHeadersRendererEntry: (typeof import("./components/lenses/HeadersRendererEntry.vue"))["default"] LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default']
LensesRenderersAudioLensRenderer: (typeof import("./components/lenses/renderers/AudioLensRenderer.vue"))["default"] LensesRenderersAudioLensRenderer: typeof import('./components/lenses/renderers/AudioLensRenderer.vue')['default']
LensesRenderersHTMLLensRenderer: (typeof import("./components/lenses/renderers/HTMLLensRenderer.vue"))["default"] LensesRenderersHTMLLensRenderer: typeof import('./components/lenses/renderers/HTMLLensRenderer.vue')['default']
LensesRenderersImageLensRenderer: (typeof import("./components/lenses/renderers/ImageLensRenderer.vue"))["default"] LensesRenderersImageLensRenderer: typeof import('./components/lenses/renderers/ImageLensRenderer.vue')['default']
LensesRenderersJSONLensRenderer: (typeof import("./components/lenses/renderers/JSONLensRenderer.vue"))["default"] LensesRenderersJSONLensRenderer: typeof import('./components/lenses/renderers/JSONLensRenderer.vue')['default']
LensesRenderersPDFLensRenderer: (typeof import("./components/lenses/renderers/PDFLensRenderer.vue"))["default"] LensesRenderersPDFLensRenderer: typeof import('./components/lenses/renderers/PDFLensRenderer.vue')['default']
LensesRenderersRawLensRenderer: (typeof import("./components/lenses/renderers/RawLensRenderer.vue"))["default"] LensesRenderersRawLensRenderer: typeof import('./components/lenses/renderers/RawLensRenderer.vue')['default']
LensesRenderersVideoLensRenderer: (typeof import("./components/lenses/renderers/VideoLensRenderer.vue"))["default"] LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default']
LensesRenderersXMLLensRenderer: (typeof import("./components/lenses/renderers/XMLLensRenderer.vue"))["default"] LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default']
LensesResponseBodyRenderer: (typeof import("./components/lenses/ResponseBodyRenderer.vue"))["default"] LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
ProfileUserDelete: (typeof import("./components/profile/UserDelete.vue"))["default"] NewCollections: typeof import('./components/new-collections/index.vue')['default']
RealtimeCommunication: (typeof import("./components/realtime/Communication.vue"))["default"] NewCollectionsRest: typeof import('./components/new-collections/rest/index.vue')['default']
RealtimeConnectionConfig: (typeof import("./components/realtime/ConnectionConfig.vue"))["default"] NewCollectionsRestCollection: typeof import('./components/new-collections/rest/Collection.vue')['default']
RealtimeLog: (typeof import("./components/realtime/Log.vue"))["default"] NewCollectionsRestRequest: typeof import('./components/new-collections/rest/Request.vue')['default']
RealtimeLogEntry: (typeof import("./components/realtime/LogEntry.vue"))["default"] ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default']
RealtimeSubscription: (typeof import("./components/realtime/Subscription.vue"))["default"] RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default']
SettingsExtension: (typeof import("./components/settings/Extension.vue"))["default"] RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default']
SettingsProxy: (typeof import("./components/settings/Proxy.vue"))["default"] RealtimeLog: typeof import('./components/realtime/Log.vue')['default']
Share: (typeof import("./components/share/index.vue"))["default"] RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default']
ShareCreateModal: (typeof import("./components/share/CreateModal.vue"))["default"] RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default']
ShareCustomizeModal: (typeof import("./components/share/CustomizeModal.vue"))["default"] SettingsExtension: typeof import('./components/settings/Extension.vue')['default']
ShareModal: (typeof import("./components/share/Modal.vue"))["default"] SettingsProxy: typeof import('./components/settings/Proxy.vue')['default']
ShareRequest: (typeof import("./components/share/Request.vue"))["default"] Share: typeof import('./components/share/index.vue')['default']
ShareTemplatesButton: (typeof import("./components/share/templates/Button.vue"))["default"] ShareCreateModal: typeof import('./components/share/CreateModal.vue')['default']
ShareTemplatesEmbeds: (typeof import("./components/share/templates/Embeds.vue"))["default"] ShareCustomizeModal: typeof import('./components/share/CustomizeModal.vue')['default']
ShareTemplatesLink: (typeof import("./components/share/templates/Link.vue"))["default"] ShareModal: typeof import('./components/share/Modal.vue')['default']
SmartAccentModePicker: (typeof import("./components/smart/AccentModePicker.vue"))["default"] ShareRequest: typeof import('./components/share/Request.vue')['default']
SmartChangeLanguage: (typeof import("./components/smart/ChangeLanguage.vue"))["default"] ShareTemplatesButton: typeof import('./components/share/templates/Button.vue')['default']
SmartColorModePicker: (typeof import("./components/smart/ColorModePicker.vue"))["default"] ShareTemplatesEmbeds: typeof import('./components/share/templates/Embeds.vue')['default']
SmartEnvInput: (typeof import("./components/smart/EnvInput.vue"))["default"] ShareTemplatesLink: typeof import('./components/share/templates/Link.vue')['default']
TabPrimary: (typeof import("./components/tab/Primary.vue"))["default"] SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default']
TabSecondary: (typeof import("./components/tab/Secondary.vue"))["default"] SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default']
Teams: (typeof import("./components/teams/index.vue"))["default"] SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default']
TeamsAdd: (typeof import("./components/teams/Add.vue"))["default"] SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default']
TeamsEdit: (typeof import("./components/teams/Edit.vue"))["default"] TabPrimary: typeof import('./components/tab/Primary.vue')['default']
TeamsInvite: (typeof import("./components/teams/Invite.vue"))["default"] TabSecondary: typeof import('./components/tab/Secondary.vue')['default']
TeamsMemberStack: (typeof import("./components/teams/MemberStack.vue"))["default"] Teams: typeof import('./components/teams/index.vue')['default']
TeamsModal: (typeof import("./components/teams/Modal.vue"))["default"] TeamsAdd: typeof import('./components/teams/Add.vue')['default']
TeamsTeam: (typeof import("./components/teams/Team.vue"))["default"] TeamsEdit: typeof import('./components/teams/Edit.vue')['default']
Tippy: (typeof import("vue-tippy"))["Tippy"] TeamsInvite: typeof import('./components/teams/Invite.vue')['default']
WorkspaceCurrent: (typeof import("./components/workspace/Current.vue"))["default"] TeamsMemberStack: typeof import('./components/teams/MemberStack.vue')['default']
WorkspaceSelector: (typeof import("./components/workspace/Selector.vue"))["default"] TeamsModal: typeof import('./components/teams/Modal.vue')['default']
TeamsTeam: typeof import('./components/teams/Team.vue')['default']
Tippy: typeof import('vue-tippy')['Tippy']
WorkspaceCurrent: typeof import('./components/workspace/Current.vue')['default']
WorkspacePersonalWorkspaceSelector: typeof import('./components/workspace/PersonalWorkspaceSelector.vue')['default']
WorkspaceSelector: typeof import('./components/workspace/Selector.vue')['default']
WorkspaceTestWorkspaceSelector: typeof import('./components/workspace/TestWorkspaceSelector.vue')['default']
} }
} }

View File

@@ -2,28 +2,26 @@
<div> <div>
<header <header
ref="headerRef" ref="headerRef"
class="grid grid-cols-5 grid-rows-1 gap-2 overflow-x-auto overflow-y-hidden p-2" class="flex flex-1 flex-shrink-0 items-center justify-between space-x-2 overflow-x-auto overflow-y-hidden px-2 py-2"
@mousedown.prevent="platform.ui?.appHeader?.onHeaderAreaClick?.()" @mousedown.prevent="platform.ui?.appHeader?.onHeaderAreaClick?.()"
> >
<div <div
class="col-span-2 flex items-center justify-between space-x-2" class="inline-flex flex-1 items-center justify-start space-x-2"
:style="{ :style="{
paddingTop: platform.ui?.appHeader?.paddingTop?.value, paddingTop: platform.ui?.appHeader?.paddingTop?.value,
paddingLeft: platform.ui?.appHeader?.paddingLeft?.value, paddingLeft: platform.ui?.appHeader?.paddingLeft?.value,
}" }"
> >
<div class="flex"> <HoppButtonSecondary
<HoppButtonSecondary class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark" :label="t('app.name')"
:label="t('app.name')" to="/"
to="/" />
/>
</div>
</div> </div>
<div class="col-span-1 flex items-center justify-between space-x-2"> <div class="inline-flex flex-1 items-center justify-center space-x-2">
<button <button
class="flex h-full flex-1 cursor-text items-center justify-between self-stretch rounded border border-dividerDark bg-primaryDark px-2 text-secondaryLight transition hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary" class="flex max-w-[15rem] flex-1 cursor-text items-center justify-between self-stretch rounded border border-dividerDark bg-primaryDark px-2 py-1 text-secondaryLight transition hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary"
@click="invokeAction('modals.search.toggle', undefined, 'mouseclick')" @click="invokeAction('modals.search.toggle')"
> >
<span class="inline-flex flex-1 items-center"> <span class="inline-flex flex-1 items-center">
<icon-lucide-search class="svg-icons mr-2" /> <icon-lucide-search class="svg-icons mr-2" />
@@ -34,188 +32,192 @@
<kbd class="shortcut-key">K</kbd> <kbd class="shortcut-key">K</kbd>
</span> </span>
</button> </button>
<HoppButtonSecondary
v-if="showInstallButton"
v-tippy="{ theme: 'tooltip' }"
:title="t('header.install_pwa')"
:icon="IconDownload"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="installPWA()"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${
mdAndLarger ? t('support.title') : t('app.options')
} <kbd>?</kbd>`"
:icon="IconLifeBuoy"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="invokeAction('modals.support.toggle')"
/>
</div> </div>
<div class="col-span-2 flex items-center justify-between space-x-2"> <div class="inline-flex flex-1 items-center justify-end space-x-2">
<div class="flex"> <div
v-if="currentUser === null"
class="inline-flex items-center space-x-2"
>
<HoppButtonSecondary <HoppButtonSecondary
v-if="showInstallButton" :icon="IconUploadCloud"
v-tippy="{ theme: 'tooltip' }" :label="t('header.save_workspace')"
:title="t('header.install_pwa')" class="py-1.75 !focus-visible:text-green-600 !hover:text-green-600 hidden border border-green-600/25 bg-green-500/[.15] !text-green-500 hover:border-green-800/50 hover:bg-green-400/10 focus-visible:border-green-800/50 focus-visible:bg-green-400/10 md:flex"
:icon="IconDownload" @click="invokeAction('modals.login.toggle')"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="installPWA()"
/> />
<HoppButtonSecondary <HoppButtonPrimary
v-tippy="{ theme: 'tooltip', allowHTML: true }" :label="t('header.login')"
:title="`${ @click="invokeAction('modals.login.toggle')"
mdAndLarger ? t('support.title') : t('app.options')
} <kbd>?</kbd>`"
:icon="IconLifeBuoy"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="invokeAction('modals.support.toggle')"
/> />
</div> </div>
<div class="flex"> <div v-else class="inline-flex items-center space-x-2">
<TeamsMemberStack
v-if="
workspace.type === 'team' &&
selectedTeam &&
selectedTeam.teamMembers.length > 1
"
:team-members="selectedTeam.teamMembers"
show-count
class="mx-2"
@handle-click="handleTeamEdit()"
/>
<div <div
v-if="currentUser === null" class="flex divide-x divide-green-600/25 rounded border border-green-600/25 bg-green-500/[.15] focus-within:divide-green-800/50 focus-within:border-green-800/50 focus-within:bg-green-400/10 hover:divide-green-800/50 hover:border-green-800/50 hover:bg-green-400/10"
class="inline-flex items-center space-x-2"
> >
<HoppButtonSecondary <HoppButtonSecondary
:icon="IconUploadCloud" v-tippy="{ theme: 'tooltip' }"
:label="t('header.save_workspace')" :title="t('team.invite_tooltip')"
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 hidden h-8 border border-emerald-600/25 bg-emerald-500/10 !text-emerald-500 hover:border-emerald-600/20 hover:bg-emerald-600/20 focus-visible:border-emerald-600/20 focus-visible:bg-emerald-600/20 md:flex" :icon="IconUserPlus"
@click="invokeAction('modals.login.toggle')" class="py-1.75 !focus-visible:text-green-600 !hover:text-green-600 !text-green-500"
@click="handleInvite()"
/> />
<HoppButtonPrimary <HoppButtonSecondary
:label="t('header.login')"
class="h-8"
@click="invokeAction('modals.login.toggle')"
/>
</div>
<div v-else class="inline-flex items-center space-x-2">
<TeamsMemberStack
v-if=" v-if="
workspace.type === 'team' && workspace.type === 'team' &&
selectedTeam && selectedTeam &&
selectedTeam.teamMembers.length > 1 selectedTeam?.myRole === 'OWNER'
" "
:team-members="selectedTeam.teamMembers" v-tippy="{ theme: 'tooltip' }"
show-count :title="t('team.edit')"
class="mx-2" :icon="IconSettings"
@handle-click="handleTeamEdit()" class="py-1.75 !focus-visible:text-green-600 !hover:text-green-600 !text-green-500"
@click="handleTeamEdit()"
/> />
<div </div>
class="flex h-8 divide-x divide-emerald-600/25 rounded border border-emerald-600/25 bg-emerald-500/10 focus-within:divide-emerald-600/20 focus-within:border-emerald-600/20 focus-within:bg-emerald-600/20 hover:divide-emerald-600/20 hover:border-emerald-600/20 hover:bg-emerald-600/20" <tippy
> interactive
<HoppButtonSecondary trigger="click"
v-tippy="{ theme: 'tooltip' }" theme="popover"
:title="t('team.invite_tooltip')" :on-shown="() => accountActions.focus()"
:icon="IconUserPlus" >
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 !text-emerald-500" <HoppButtonSecondary
@click="handleInvite()" v-tippy="{ theme: 'tooltip' }"
/> :title="t('workspace.change')"
<HoppButtonSecondary :label="mdAndLarger ? activeWorkspaceName : ``"
v-if=" :icon="activeWorkspaceIcon"
workspace.type === 'team' && class="select-wrapper !focus-visible:text-blue-600 !hover:text-blue-600 rounded border border-blue-600/25 bg-blue-500/[.15] py-[0.4375rem] pr-8 !text-blue-500 hover:border-blue-800/50 hover:bg-blue-400/10 focus-visible:border-blue-800/50 focus-visible:bg-blue-400/10"
selectedTeam && />
selectedTeam?.myRole === 'OWNER' <template #content="{ hide }">
" <div
v-tippy="{ theme: 'tooltip' }" ref="accountActions"
:title="t('team.edit')" class="flex flex-col focus:outline-none"
:icon="IconSettings" tabindex="0"
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 !text-emerald-500" @keyup.escape="hide()"
@click="handleTeamEdit()" @click="hide()"
/> >
</div> <WorkspaceSelector />
</div>
</template>
</tippy>
<span class="px-2">
<tippy <tippy
interactive interactive
trigger="click" trigger="click"
theme="popover" theme="popover"
:on-shown="() => accountActions.focus()" :on-shown="() => tippyActions.focus()"
> >
<HoppSmartSelectWrapper <HoppSmartPicture
class="!text-blue-500 !focus-visible:text-blue-600 !hover:text-blue-600" v-if="currentUser.photoURL"
> v-tippy="{
<HoppButtonSecondary theme: 'tooltip',
v-tippy="{ theme: 'tooltip' }" }"
:title="t('workspace.change')" :url="currentUser.photoURL"
:label="mdAndLarger ? workspaceName : ``" :alt="
:icon="workspace.type === 'personal' ? IconUser : IconUsers" currentUser.displayName ||
class="!focus-visible:text-blue-600 !hover:text-blue-600 h-8 rounded border border-blue-600/25 bg-blue-500/10 pr-8 !text-blue-500 hover:border-blue-600/20 hover:bg-blue-600/20 focus-visible:border-blue-600/20 focus-visible:bg-blue-600/20" t('profile.default_hopp_displayname')
/> "
</HoppSmartSelectWrapper> :title="
currentUser.displayName ||
currentUser.email ||
t('profile.default_hopp_displayname')
"
indicator
:indicator-styles="
network.isOnline ? 'bg-green-500' : 'bg-red-500'
"
/>
<HoppSmartPicture
v-else
v-tippy="{ theme: 'tooltip' }"
:title="
currentUser.displayName ||
currentUser.email ||
t('profile.default_hopp_displayname')
"
:initial="currentUser.displayName || currentUser.email"
indicator
:indicator-styles="
network.isOnline ? 'bg-green-500' : 'bg-red-500'
"
/>
<template #content="{ hide }"> <template #content="{ hide }">
<div <div
ref="accountActions" ref="tippyActions"
class="flex flex-col focus:outline-none" class="flex flex-col focus:outline-none"
tabindex="0" tabindex="0"
@keyup.p="profile.$el.click()"
@keyup.s="settings.$el.click()"
@keyup.l="logout.$el.click()"
@keyup.escape="hide()" @keyup.escape="hide()"
@click="hide()"
> >
<WorkspaceSelector /> <div class="flex flex-col px-2 text-tiny">
<span class="inline-flex truncate font-semibold">
{{
currentUser.displayName ||
t("profile.default_hopp_displayname")
}}
</span>
<span class="inline-flex truncate text-secondaryLight">
{{ currentUser.email }}
</span>
</div>
<hr />
<HoppSmartItem
ref="profile"
to="/profile"
:icon="IconUser"
:label="t('navigation.profile')"
:shortcut="['P']"
@click="hide()"
/>
<HoppSmartItem
ref="settings"
to="/settings"
:icon="IconSettings"
:label="t('navigation.settings')"
:shortcut="['S']"
@click="hide()"
/>
<FirebaseLogout
ref="logout"
:shortcut="['L']"
@confirm-logout="hide()"
/>
</div> </div>
</template> </template>
</tippy> </tippy>
<span class="px-2"> </span>
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions.focus()"
>
<HoppSmartPicture
v-tippy="{
theme: 'tooltip',
}"
:name="currentUser.uid"
:title="
currentUser.displayName ||
currentUser.email ||
t('profile.default_hopp_displayname')
"
indicator
:indicator-styles="
network.isOnline ? 'bg-green-500' : 'bg-red-500'
"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.p="profile.$el.click()"
@keyup.s="settings.$el.click()"
@keyup.l="logout.$el.click()"
@keyup.escape="hide()"
>
<div class="flex flex-col px-2">
<span class="inline-flex truncate font-semibold">
{{
currentUser.displayName ||
t("profile.default_hopp_displayname")
}}
</span>
<span
class="inline-flex truncate text-secondaryLight text-tiny"
>
{{ currentUser.email }}
</span>
</div>
<hr />
<HoppSmartItem
ref="profile"
to="/profile"
:icon="IconUser"
:label="t('navigation.profile')"
:shortcut="['P']"
@click="hide()"
/>
<HoppSmartItem
ref="settings"
to="/settings"
:icon="IconSettings"
:label="t('navigation.settings')"
:shortcut="['S']"
@click="hide()"
/>
<FirebaseLogout
ref="logout"
:shortcut="['L']"
@confirm-logout="hide()"
/>
</div>
</template>
</tippy>
</span>
</div>
</div> </div>
</div> </div>
</header> </header>
<AppBanner <AppBanner v-if="bannerContent" :banner="bannerContent" />
v-if="bannerContent"
:banner="bannerContent"
@dismiss="dismissOfflineBanner"
/>
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" /> <TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
<TeamsInvite <TeamsInvite
v-if="workspace.type === 'team' && workspace.teamID" v-if="workspace.type === 'team' && workspace.teamID"
@@ -231,6 +233,7 @@
@invite-team="inviteTeam(editingTeamName, editingTeamID)" @invite-team="inviteTeam(editingTeamName, editingTeamID)"
@refetch-teams="refetchTeams" @refetch-teams="refetchTeams"
/> />
<HoppSmartConfirmModal <HoppSmartConfirmModal
:show="confirmRemove" :show="confirmRemove"
:title="t('confirm.remove_team')" :title="t('confirm.remove_team')"
@@ -244,30 +247,31 @@
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { useReadonlyStream } from "@composables/stream" import { useReadonlyStream } from "@composables/stream"
import { defineActionHandler, invokeAction } from "@helpers/actions" import { defineActionHandler, invokeAction } from "@helpers/actions"
import { WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue"
import { installPWA, pwaDefferedPrompt } from "@modules/pwa" import { installPWA, pwaDefferedPrompt } from "@modules/pwa"
import { breakpointsTailwind, useBreakpoints, useNetwork } from "@vueuse/core" import { breakpointsTailwind, useBreakpoints, useNetwork } from "@vueuse/core"
import { useService } from "dioc/vue"
import * as TE from "fp-ts/TaskEither"
import { pipe } from "fp-ts/function"
import { computed, reactive, ref, watch } from "vue" import { computed, reactive, ref, watch } from "vue"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { GetMyTeamsQuery, TeamMemberRole } from "~/helpers/backend/graphql" import { GetMyTeamsQuery, TeamMemberRole } from "~/helpers/backend/graphql"
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
import { getPlatformSpecialKey } from "~/helpers/platformutils" import { getPlatformSpecialKey } from "~/helpers/platformutils"
import { platform } from "~/platform" import { platform } from "~/platform"
import {
BANNER_PRIORITY_HIGH,
BannerContent,
BannerService,
} from "~/services/banner.service"
import { NewWorkspaceService } from "~/services/new-workspace"
import { WorkspaceService } from "~/services/workspace.service"
import IconDownload from "~icons/lucide/download" import IconDownload from "~icons/lucide/download"
import IconLifeBuoy from "~icons/lucide/life-buoy" import IconLifeBuoy from "~icons/lucide/life-buoy"
import IconSettings from "~icons/lucide/settings" import IconSettings from "~icons/lucide/settings"
import IconUploadCloud from "~icons/lucide/upload-cloud" import IconUploadCloud from "~icons/lucide/upload-cloud"
import IconUser from "~icons/lucide/user" import IconUser from "~icons/lucide/user"
import IconUserPlus from "~icons/lucide/user-plus" import IconUserPlus from "~icons/lucide/user-plus"
import IconUsers from "~icons/lucide/users"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
import {
BannerService,
BannerContent,
BANNER_PRIORITY_HIGH,
} from "~/services/banner.service"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
@@ -290,11 +294,10 @@ const bannerContent = computed(() => banner.content.value?.content)
let bannerID: number | null = null let bannerID: number | null = null
const offlineBanner: BannerContent = { const offlineBanner: BannerContent = {
type: "warning", type: "info",
text: (t) => t("helpers.offline"), text: (t) => t("helpers.offline"),
alternateText: (t) => t("helpers.offline_short"), alternateText: (t) => t("helpers.offline_short"),
score: BANNER_PRIORITY_HIGH, score: BANNER_PRIORITY_HIGH,
dismissible: true,
} }
const network = reactive(useNetwork()) const network = reactive(useNetwork())
@@ -311,8 +314,6 @@ watch(isOnline, () => {
} }
}) })
const dismissOfflineBanner = () => banner.removeBanner(bannerID!)
const currentUser = useReadonlyStream( const currentUser = useReadonlyStream(
platform.auth.getProbableUserStream(), platform.auth.getProbableUserStream(),
platform.auth.getProbableUser() platform.auth.getProbableUser()
@@ -330,12 +331,6 @@ const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
const workspace = workspaceService.currentWorkspace const workspace = workspaceService.currentWorkspace
const workspaceName = computed(() => {
return workspace.value.type === "personal"
? t("workspace.personal")
: workspace.value.teamName
})
const refetchTeams = () => { const refetchTeams = () => {
teamListAdapter.fetchList() teamListAdapter.fetchList()
} }
@@ -370,6 +365,23 @@ watch(
} }
) )
const newWorkspaceService = useService(NewWorkspaceService)
const activeWorkspaceName = computed(() => {
const activeWorkspaceHandleRef =
newWorkspaceService.activeWorkspaceHandle.value?.get()
if (activeWorkspaceHandleRef?.value.type === "ok") {
return activeWorkspaceHandleRef.value.data.name
}
return t("workspace.no_workspace")
})
const activeWorkspaceIcon = computed(() => {
return newWorkspaceService.activeWorkspaceDecor.value?.value.headerCurrentIcon
})
const showModalInvite = ref(false) const showModalInvite = ref(false)
const showModalEdit = ref(false) const showModalEdit = ref(false)
@@ -393,6 +405,8 @@ const inviteTeam = (team: { name: string }, teamID: string) => {
// Show the workspace selected team invite modal if the user is an owner of the team else show the default invite modal // Show the workspace selected team invite modal if the user is an owner of the team else show the default invite modal
const handleInvite = () => { const handleInvite = () => {
if (!currentUser.value) return invokeAction("modals.login.toggle")
if ( if (
workspace.value.type === "team" && workspace.value.type === "team" &&
workspace.value.teamID && workspace.value.teamID &&

View File

@@ -0,0 +1,135 @@
<template>
<div
class="border-animation relative p-[1px] rounded flex-1 self-stretch overflow-hidden flex items-center justify-center"
:class="{
'before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:aspect-square before:w-full before:absolute before:bg-':
!HAS_OPENED_SPOTLIGHT,
}"
aria-hidden="true"
>
<button
class="relative flex flex-1 cursor-text items-center justify-between self-stretch rounded bg-primaryDark px-2 text-secondaryLight transition hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary overflow-hidden"
@click="
() => {
invokeAction('modals.search.toggle', undefined, 'mouseclick')
!HAS_OPENED_SPOTLIGHT && toggleSetting('HAS_OPENED_SPOTLIGHT')
}
"
>
<span class="inline-flex flex-1 items-center">
<icon-lucide-search class="svg-icons mr-2" />
<span v-if="!HAS_OPENED_SPOTLIGHT" class="flex flex-1">
{{ t("spotlight.phrases.try") }}
<TransitionGroup tag="div" name="list" class="ml-1 relative">
<span
v-for="(phrase, index) in phraseToShow"
:key="phrase.text"
:data-index="index"
class="truncate"
>
"{{ t(phrase.text) }}"
</span>
</TransitionGroup>
</span>
<template v-else>
{{ t("app.search") }}
</template>
</span>
<span class="flex space-x-1">
<kbd class="shortcut-key">{{ getPlatformSpecialKey() }}</kbd>
<kbd class="shortcut-key">K</kbd>
</span>
</button>
</div>
</template>
<script lang="ts" setup>
import { watch, computed, ref } from "vue"
import { useI18n } from "~/composables/i18n"
import { useSetting } from "~/composables/settings"
import { invokeAction } from "~/helpers/actions"
import { getPlatformSpecialKey } from "~/helpers/platformutils"
import { toggleSetting } from "~/newstore/settings"
const t = useI18n()
const HAS_OPENED_SPOTLIGHT = useSetting("HAS_OPENED_SPOTLIGHT")
const phrases = ref([
{ text: "spotlight.phrases.import_collections", show: true },
{ text: "spotlight.phrases.create_environment", show: false },
{ text: "spotlight.phrases.create_workspace", show: false },
{ text: "spotlight.phrases.share_request", show: false },
])
let intervalId: ReturnType<typeof setTimeout> | null = null
//cycle through the phrases
const showNextPhrase = () => {
let i = 0
intervalId = setInterval(() => {
phrases.value[i].show = false
i++
if (i >= phrases.value.length) {
i = 0
}
phrases.value[i].show = true
}, 3000)
}
const stopPhraseInterval = () => {
if (intervalId) clearInterval(intervalId)
}
const phraseToShow = computed(() => {
return phrases.value.filter((phrase) => phrase.show)
})
watch(
HAS_OPENED_SPOTLIGHT,
() => {
!HAS_OPENED_SPOTLIGHT.value ? showNextPhrase() : stopPhraseInterval()
},
{
immediate: true,
}
)
</script>
<style>
/* Transition Classes */
.list-enter-active {
transition: all 1s ease;
}
.list-leave-active {
transition: all 0.4s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(-30px);
}
.list-leave-active {
position: absolute;
}
/* Conic gradient */
.border-animation::before {
background: conic-gradient(
transparent 270deg,
var(--accent-color),
transparent
);
animation: rotate 4s linear infinite;
}
@keyframes rotate {
from {
transform: translate(-50%, -50%) scale(1.4) rotate(0turn);
}
to {
transform: translate(-50%, -50%) scale(1.4) rotate(1turn);
}
}
</style>

View File

@@ -30,9 +30,8 @@ import { defineStep } from "~/composables/step-components"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { appendRESTCollections, restCollections$ } from "~/newstore/collections" import { restCollections$ } from "~/newstore/collections"
import MyCollectionImport from "~/components/importExport/ImportExportSteps/MyCollectionImport.vue" import MyCollectionImport from "~/components/importExport/ImportExportSteps/MyCollectionImport.vue"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import IconFolderPlus from "~icons/lucide/folder-plus" import IconFolderPlus from "~icons/lucide/folder-plus"
import IconOpenAPI from "~icons/lucide/file" import IconOpenAPI from "~icons/lucide/file"
@@ -48,23 +47,23 @@ import { getTeamCollectionJSON } from "~/helpers/backend/helpers"
import { platform } from "~/platform" import { platform } from "~/platform"
import { initializeDownloadCollection } from "~/helpers/import-export/export" import { initializeDownloadFile } from "~/helpers/import-export/export"
import { gistExporter } from "~/helpers/import-export/export/gist" import { gistExporter } from "~/helpers/import-export/export/gist"
import { myCollectionsExporter } from "~/helpers/import-export/export/myCollections"
import { teamCollectionsExporter } from "~/helpers/import-export/export/teamCollections" import { teamCollectionsExporter } from "~/helpers/import-export/export/teamCollections"
import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource" import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource"
import { ImporterOrExporter } from "~/components/importExport/types" import { ImporterOrExporter } from "~/components/importExport/types"
import { useService } from "dioc/vue"
import { NewWorkspaceService } from "~/services/new-workspace"
import { TeamWorkspace } from "~/services/workspace.service"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
type CollectionType = type CollectionType =
| { | {
type: "team-collections" type: "team-collections"
selectedTeam: SelectedTeam selectedTeam: TeamWorkspace
} }
| { type: "my-collections" } | { type: "my-collections" }
@@ -86,15 +85,45 @@ const currentUser = useReadonlyStream(
const myCollections = useReadonlyStream(restCollections$, []) const myCollections = useReadonlyStream(restCollections$, [])
const workspaceService = useService(NewWorkspaceService)
const activeWorkspaceHandle = workspaceService.activeWorkspaceHandle
const showImportFailedError = () => { const showImportFailedError = () => {
toast.error(t("import.failed")) toast.error(t("import.failed"))
} }
const handleImportToStore = async (collections: HoppCollection[]) => { const handleImportToStore = async (collections: HoppCollection[]) => {
const importResult = if (props.collectionsType.type === "my-collections") {
props.collectionsType.type === "my-collections" if (!activeWorkspaceHandle.value) {
? await importToPersonalWorkspace(collections) return
: await importToTeamsWorkspace(collections) }
const collectionHandleResult = await workspaceService.importRESTCollections(
activeWorkspaceHandle.value,
collections
)
if (E.isLeft(collectionHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return toast.error(t("import.failed"))
}
const resultHandle = collectionHandleResult.right
const requestHandleRef = resultHandle.get()
if (requestHandleRef.value.type === "invalid") {
// WORKSPACE_INVALIDATED
}
toast.success(t("state.file_imported"))
emit("hide-modal")
return
}
const importResult = await importToTeamsWorkspace(collections)
if (E.isRight(importResult)) { if (E.isRight(importResult)) {
toast.success(t("state.file_imported")) toast.success(t("state.file_imported"))
@@ -104,13 +133,6 @@ const handleImportToStore = async (collections: HoppCollection[]) => {
} }
} }
const importToPersonalWorkspace = (collections: HoppCollection[]) => {
appendRESTCollections(collections)
return E.right({
success: true,
})
}
function translateToTeamCollectionFormat(x: HoppCollection) { function translateToTeamCollectionFormat(x: HoppCollection) {
const folders: HoppCollection[] = (x.folders ?? []).map( const folders: HoppCollection[] = (x.folders ?? []).map(
translateToTeamCollectionFormat translateToTeamCollectionFormat
@@ -390,28 +412,34 @@ const HoppMyCollectionsExporter: ImporterOrExporter = {
applicableTo: ["personal-workspace"], applicableTo: ["personal-workspace"],
isLoading: isHoppMyCollectionExporterInProgress, isLoading: isHoppMyCollectionExporterInProgress,
}, },
action: () => { action: async () => {
if (!myCollections.value.length) { if (!myCollections.value.length) {
return toast.error(t("error.no_collections_to_export")) return toast.error(t("error.no_collections_to_export"))
} }
if (!activeWorkspaceHandle.value) {
return
}
isHoppMyCollectionExporterInProgress.value = true isHoppMyCollectionExporterInProgress.value = true
const message = initializeDownloadCollection( const result = await workspaceService.exportRESTCollections(
myCollectionsExporter(myCollections.value), activeWorkspaceHandle.value,
"Collections" myCollections.value
) )
if (E.isRight(message)) { if (E.isLeft(result)) {
toast.success(t(message.right)) // INVALID_WORKSPACE_HANDLE
platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION",
exporter: "json",
platform: "rest",
})
} }
toast.success(t("state.download_started"))
platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION",
exporter: "json",
platform: "rest",
})
isHoppMyCollectionExporterInProgress.value = false isHoppMyCollectionExporterInProgress.value = false
}, },
} }
@@ -433,7 +461,7 @@ const HoppTeamCollectionsExporter: ImporterOrExporter = {
props.collectionsType.selectedTeam props.collectionsType.selectedTeam
) { ) {
const res = await teamCollectionsExporter( const res = await teamCollectionsExporter(
props.collectionsType.selectedTeam.id props.collectionsType.selectedTeam.teamID
) )
if (E.isRight(res)) { if (E.isRight(res)) {
@@ -445,10 +473,7 @@ const HoppTeamCollectionsExporter: ImporterOrExporter = {
return toast.error(t("error.no_collections_to_export")) return toast.error(t("error.no_collections_to_export"))
} }
initializeDownloadCollection( initializeDownloadFile(exportCollectionsToJSON, "team-collections")
exportCollectionsToJSON,
"team-collections"
)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION", type: "HOPP_EXPORT_COLLECTION",
@@ -487,7 +512,7 @@ const HoppGistCollectionsExporter: ImporterOrExporter = {
const collectionJSON = await getCollectionJSON() const collectionJSON = await getCollectionJSON()
const accessToken = currentUser.value?.accessToken const accessToken = currentUser.value?.accessToken
if (!accessToken) { if (!accessToken || E.isLeft(collectionJSON)) {
toast.error(t("error.something_went_wrong")) toast.error(t("error.something_went_wrong"))
isHoppGistCollectionExporterInProgress.value = false isHoppGistCollectionExporterInProgress.value = false
return return
@@ -569,8 +594,8 @@ const hasTeamWriteAccess = computed(() => {
} }
return ( return (
collectionsType.selectedTeam.myRole === "EDITOR" || collectionsType.selectedTeam.role === "EDITOR" ||
collectionsType.selectedTeam.myRole === "OWNER" collectionsType.selectedTeam.role === "OWNER"
) )
}) })
@@ -578,26 +603,49 @@ const selectedTeamID = computed(() => {
const { collectionsType } = props const { collectionsType } = props
return collectionsType.type === "team-collections" return collectionsType.type === "team-collections"
? collectionsType.selectedTeam?.id ? collectionsType.selectedTeam?.teamID
: undefined : undefined
}) })
const getCollectionJSON = async () => { const getCollectionJSON = async () => {
// TODO: Implement `getRESTCollectionJSONView` for team workspace
if ( if (
props.collectionsType.type === "team-collections" && props.collectionsType.type === "team-collections" &&
props.collectionsType.selectedTeam?.id props.collectionsType.selectedTeam?.teamID
) { ) {
const res = await getTeamCollectionJSON( const res = await getTeamCollectionJSON(
props.collectionsType.selectedTeam?.id props.collectionsType.selectedTeam?.teamID
) )
return E.isRight(res) return E.isRight(res)
? E.right(res.right.exportCollectionsToJSON) ? E.right(res.right.exportCollectionsToJSON)
: E.left(res.left) : E.left(res.left.error.toString())
} }
if (props.collectionsType.type === "my-collections") { if (props.collectionsType.type === "my-collections") {
return E.right(JSON.stringify(myCollections.value, null, 2)) if (!activeWorkspaceHandle.value) {
return E.left("INVALID_WORKSPACE_HANDLE")
}
const collectionJSONHandleResult =
await workspaceService.getRESTCollectionJSONView(
activeWorkspaceHandle.value
)
if (E.isLeft(collectionJSONHandleResult)) {
return E.left(collectionJSONHandleResult.left.error)
}
const collectionJSONHandle = collectionJSONHandleResult.right
const collectionJSONHandleRef = collectionJSONHandle.get()
if (collectionJSONHandleRef.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return E.left("WORKSPACE_INVALIDATED")
}
return E.right(collectionJSONHandleRef.value.data.content)
} }
return E.left("INVALID_SELECTED_TEAM_OR_INVALID_COLLECTION_TYPE") return E.left("INVALID_SELECTED_TEAM_OR_INVALID_COLLECTION_TYPE")

View File

@@ -75,7 +75,7 @@ import {
import { useVModel } from "@vueuse/core" import { useVModel } from "@vueuse/core"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { clone } from "lodash-es" import { clone } from "lodash-es"
import { ref, watch } from "vue" import { ref, toRefs, watch } from "vue"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
import { PersistenceService } from "~/services/persistence" import { PersistenceService } from "~/services/persistence"
@@ -100,11 +100,15 @@ const props = withDefaults(
editingProperties: EditingProperties | null editingProperties: EditingProperties | null
source: "REST" | "GraphQL" source: "REST" | "GraphQL"
modelValue: string modelValue: string
// TODO: Purpose of this prop is to maintain backwards compatibility
// To be removed after porting all usages of this component
emitWithFullCollection: boolean
}>(), }>(),
{ {
show: false, show: false,
loadingState: false, loadingState: false,
editingProperties: null, editingProperties: null,
emitWithFullCollection: true,
} }
) )
@@ -178,15 +182,24 @@ watch(
const saveEditedCollection = () => { const saveEditedCollection = () => {
if (!props.editingProperties) return if (!props.editingProperties) return
const finalCollection = clone(editableCollection.value) const finalCollection = clone(editableCollection.value)
delete finalCollection.body
const { path } = toRefs(props.editingProperties)
const collection = { const collection = {
path: props.editingProperties.path, path: path.value,
collection: { collection: {
...props.editingProperties.collection, ...props.editingProperties.collection,
...finalCollection, ...finalCollection,
}, },
isRootCollection: props.editingProperties.isRootCollection, isRootCollection: props.editingProperties.isRootCollection,
} }
emit("set-collection-properties", collection as EditingProperties)
const data = props.emitWithFullCollection
? collection
: { ...finalCollection, collIndexPath: path.value }
emit("set-collection-properties", data as EditingProperties)
persistenceService.removeLocalConfig("unsaved_collection_properties") persistenceService.removeLocalConfig("unsaved_collection_properties")
} }

View File

@@ -20,19 +20,25 @@
<label class="p-4"> <label class="p-4">
{{ t("collection.select_location") }} {{ t("collection.select_location") }}
</label> </label>
<CollectionsGraphql <!-- <CollectionsGraphql
v-if="mode === 'graphql'" v-if="mode === 'graphql'"
:picked="picked" :picked="picked"
:save-request="true" :save-request="true"
@select="onSelect" @select="onSelect"
/> /> -->
<Collections <!-- <Collections
v-else v-else
:picked="picked" :picked="picked"
:save-request="true" :save-request="true"
@select="onSelect" @select="onSelect"
@update-team="updateTeam" @update-team="updateTeam"
@update-collection-type="updateCollectionType" @update-collection-type="updateCollectionType"
/> -->
<NewCollections
:picked="picked"
:save-request="true"
platform="rest"
@select="onSelect"
/> />
</div> </div>
</template> </template>
@@ -56,51 +62,40 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from "vue" import { useI18n } from "@composables/i18n"
import { cloneDeep } from "lodash-es" import { useToast } from "@composables/toast"
import { import {
HoppGQLRequest, HoppGQLRequest,
HoppRESTRequest, HoppRESTRequest,
isHoppRESTRequest, isHoppRESTRequest,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import {
createRequestInCollection,
updateTeamRequest,
} from "~/helpers/backend/mutations/TeamRequest"
import { Picked } from "~/helpers/types/HoppPicked"
import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast"
import {
cascadeParentCollectionForHeaderAuth,
editGraphqlRequest,
editRESTRequest,
saveGraphqlRequestAs,
saveRESTRequestAs,
} from "~/newstore/collections"
import { GQLError } from "~/helpers/backend/GQLClient"
import { computedWithControl } from "@vueuse/core" import { computedWithControl } from "@vueuse/core"
import { platform } from "~/platform"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { RESTTabService } from "~/services/tab/rest" import * as E from "fp-ts/Either"
import { cloneDeep } from "lodash-es"
import { computed, nextTick, reactive, ref, watch } from "vue"
import { Picked } from "~/helpers/types/HoppPicked"
import { cascadeParentCollectionForHeaderAuth } from "~/newstore/collections"
import { NewWorkspaceService } from "~/services/new-workspace"
import { GQLTabService } from "~/services/tab/graphql" import { GQLTabService } from "~/services/tab/graphql"
import { RESTTabService } from "~/services/tab/rest"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
const RESTTabs = useService(RESTTabService) const RESTTabs = useService(RESTTabService)
const GQLTabs = useService(GQLTabService) const GQLTabs = useService(GQLTabService)
const workspaceService = useService(NewWorkspaceService)
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined // type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
type CollectionType = // type CollectionType =
| { // | {
type: "team-collections" // type: "team-collections"
selectedTeam: SelectedTeam // selectedTeam: SelectedTeam
} // }
| { type: "my-collections"; selectedTeam: undefined } // | { type: "my-collections"; selectedTeam: undefined }
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -168,10 +163,10 @@ const requestData = reactive({
requestIndex: undefined as number | undefined, requestIndex: undefined as number | undefined,
}) })
const collectionsType = ref<CollectionType>({ // const collectionsType = ref<CollectionType>({
type: "my-collections", // type: "my-collections",
selectedTeam: undefined, // selectedTeam: undefined,
}) // })
const picked = ref<Picked | null>(null) const picked = ref<Picked | null>(null)
@@ -192,13 +187,14 @@ watch(
} }
) )
const updateTeam = (newTeam: SelectedTeam) => { // TODO: To be removed
collectionsType.value.selectedTeam = newTeam // const updateTeam = (newTeam: SelectedTeam) => {
} // collectionsType.value.selectedTeam = newTeam
// }
const updateCollectionType = (type: CollectionType["type"]) => { // const updateCollectionType = (type: CollectionType["type"]) => {
collectionsType.value.type = type // collectionsType.value.type = type
} // }
const onSelect = (pickedVal: Picked | null) => { const onSelect = (pickedVal: Picked | null) => {
picked.value = pickedVal picked.value = pickedVal
@@ -214,104 +210,109 @@ const saveRequestAs = async () => {
return return
} }
const requestUpdated = const updatedRequest =
props.mode === "rest" props.mode === "rest"
? cloneDeep(RESTTabs.currentActiveTab.value.document.request) ? cloneDeep(RESTTabs.currentActiveTab.value.document.request)
: cloneDeep(GQLTabs.currentActiveTab.value.document.request) : cloneDeep(GQLTabs.currentActiveTab.value.document.request)
requestUpdated.name = requestName.value updatedRequest.name = requestName.value
if (picked.value.pickedType === "my-collection") { if (!workspaceService.activeWorkspaceHandle.value) {
if (!isHoppRESTRequest(requestUpdated)) return
}
if (
picked.value.pickedType === "my-collection" ||
picked.value.pickedType === "my-folder"
) {
if (!isHoppRESTRequest(updatedRequest))
throw new Error("requestUpdated is not a REST Request") throw new Error("requestUpdated is not a REST Request")
const insertionIndex = saveRESTRequestAs( const collectionPathIndex =
`${picked.value.collectionIndex}`, picked.value.pickedType === "my-collection"
requestUpdated ? picked.value.collectionIndex.toString()
: picked.value.folderPath
const collectionHandleResult = await workspaceService.getCollectionHandle(
workspaceService.activeWorkspaceHandle.value,
collectionPathIndex
) )
if (E.isLeft(collectionHandleResult)) {
// INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
return
}
const collectionHandle = collectionHandleResult.right
const requestHandleResult = await workspaceService.createRESTRequest(
collectionHandle,
updatedRequest
)
if (E.isLeft(requestHandleResult)) {
// WORKSPACE_INVALIDATED | INVALID_COLLECTION_HANDLE
return
}
const requestHandle = requestHandleResult.right
const requestHandleRef = requestHandle.get()
if (requestHandleRef.value.type === "invalid") {
// WORKSPACE_INVALIDATED | INVALID_COLLECTION_HANDLE
return
}
RESTTabs.currentActiveTab.value.document = { RESTTabs.currentActiveTab.value.document = {
request: requestUpdated, request: updatedRequest,
isDirty: false, isDirty: false,
saveContext: { saveContext: {
originLocation: "user-collection", originLocation: "workspace-user-collection",
folderPath: `${picked.value.collectionIndex}`, requestHandle,
requestIndex: insertionIndex,
}, },
} }
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
`${picked.value.collectionIndex}`,
"rest"
)
RESTTabs.currentActiveTab.value.document.inheritedProperties = {
auth,
headers,
}
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: true,
platform: "rest",
workspaceType: "personal",
})
requestSaved()
} else if (picked.value.pickedType === "my-folder") {
if (!isHoppRESTRequest(requestUpdated))
throw new Error("requestUpdated is not a REST Request")
const insertionIndex = saveRESTRequestAs(
picked.value.folderPath,
requestUpdated
)
RESTTabs.currentActiveTab.value.document = {
request: requestUpdated,
isDirty: false,
saveContext: {
originLocation: "user-collection",
folderPath: picked.value.folderPath,
requestIndex: insertionIndex,
},
}
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
picked.value.folderPath,
"rest"
)
RESTTabs.currentActiveTab.value.document.inheritedProperties = {
auth,
headers,
}
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: true,
platform: "rest",
workspaceType: "personal",
})
requestSaved() requestSaved()
} else if (picked.value.pickedType === "my-request") { } else if (picked.value.pickedType === "my-request") {
if (!isHoppRESTRequest(requestUpdated)) if (!isHoppRESTRequest(updatedRequest))
throw new Error("requestUpdated is not a REST Request") throw new Error("requestUpdated is not a REST Request")
editRESTRequest( const requestHandleResult = await workspaceService.getRequestHandle(
picked.value.folderPath, workspaceService.activeWorkspaceHandle.value,
picked.value.requestIndex, `${picked.value.folderPath}/${picked.value.requestIndex.toString()}`
requestUpdated
) )
if (E.isLeft(requestHandleResult)) {
// INVALID_COLLECTION_HANDLE | INVALID_REQUEST_ID | REQUEST_NOT_FOUND
return
}
const requestHandle = requestHandleResult.right
const requestHandleRef = requestHandle.get()
if (requestHandleRef.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const updateRequestResult = await workspaceService.updateRESTRequest(
requestHandle,
updatedRequest
)
if (E.isLeft(updateRequestResult)) {
// WORKSPACE_INVALIDATED | INVALID_REQUEST_HANDLE
return
}
RESTTabs.currentActiveTab.value.document = { RESTTabs.currentActiveTab.value.document = {
request: requestUpdated, request: updatedRequest,
isDirty: false, isDirty: false,
saveContext: { saveContext: {
originLocation: "user-collection", originLocation: "workspace-user-collection",
folderPath: picked.value.folderPath, requestHandle,
requestIndex: picked.value.requestIndex,
}, },
} }
@@ -325,152 +326,147 @@ const saveRequestAs = async () => {
headers, headers,
} }
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: false,
platform: "rest",
workspaceType: "personal",
})
requestSaved()
} else if (picked.value.pickedType === "teams-collection") {
if (!isHoppRESTRequest(requestUpdated))
throw new Error("requestUpdated is not a REST Request")
updateTeamCollectionOrFolder(picked.value.collectionID, requestUpdated)
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: true,
platform: "rest",
workspaceType: "team",
})
} else if (picked.value.pickedType === "teams-folder") {
if (!isHoppRESTRequest(requestUpdated))
throw new Error("requestUpdated is not a REST Request")
updateTeamCollectionOrFolder(picked.value.folderID, requestUpdated)
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: true,
platform: "rest",
workspaceType: "team",
})
} else if (picked.value.pickedType === "teams-request") {
if (!isHoppRESTRequest(requestUpdated))
throw new Error("requestUpdated is not a REST Request")
if (
collectionsType.value.type !== "team-collections" ||
!collectionsType.value.selectedTeam
)
throw new Error("Collections Type mismatch")
modalLoadingState.value = true
const data = {
request: JSON.stringify(requestUpdated),
title: requestUpdated.name,
}
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: false,
platform: "rest",
workspaceType: "team",
})
pipe(
updateTeamRequest(picked.value.requestID, data),
TE.match(
(err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false
},
() => {
modalLoadingState.value = false
requestSaved()
}
)
)()
} else if (picked.value.pickedType === "gql-my-request") {
// TODO: Check for GQL request ?
editGraphqlRequest(
picked.value.folderPath,
picked.value.requestIndex,
requestUpdated as HoppGQLRequest
)
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: false,
platform: "gql",
workspaceType: "team",
})
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
picked.value.folderPath,
"graphql"
)
GQLTabs.currentActiveTab.value.document.inheritedProperties = {
auth,
headers,
}
requestSaved()
} else if (picked.value.pickedType === "gql-my-folder") {
// TODO: Check for GQL request ?
saveGraphqlRequestAs(
picked.value.folderPath,
requestUpdated as HoppGQLRequest
)
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: true,
platform: "gql",
workspaceType: "team",
})
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
picked.value.folderPath,
"graphql"
)
GQLTabs.currentActiveTab.value.document.inheritedProperties = {
auth,
headers,
}
requestSaved()
} else if (picked.value.pickedType === "gql-my-collection") {
// TODO: Check for GQL request ?
saveGraphqlRequestAs(
`${picked.value.collectionIndex}`,
requestUpdated as HoppGQLRequest
)
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
createdNow: true,
platform: "gql",
workspaceType: "team",
})
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
`${picked.value.collectionIndex}`,
"graphql"
)
GQLTabs.currentActiveTab.value.document.inheritedProperties = {
auth,
headers,
}
requestSaved() requestSaved()
} }
// TODO: To be removed
// else if (picked.value.pickedType === "teams-collection") {
// if (!isHoppRESTRequest(updatedRequest))
// throw new Error("requestUpdated is not a REST Request")
// updateTeamCollectionOrFolder(picked.value.collectionID, updatedRequest)
// platform.analytics?.logEvent({
// type: "HOPP_SAVE_REQUEST",
// createdNow: true,
// platform: "rest",
// workspaceType: "team",
// })
// } else if (picked.value.pickedType === "teams-folder") {
// if (!isHoppRESTRequest(updatedRequest))
// throw new Error("requestUpdated is not a REST Request")
// updateTeamCollectionOrFolder(picked.value.folderID, updatedRequest)
// platform.analytics?.logEvent({
// type: "HOPP_SAVE_REQUEST",
// createdNow: true,
// platform: "rest",
// workspaceType: "team",
// })
// } else if (picked.value.pickedType === "teams-request") {
// if (!isHoppRESTRequest(updatedRequest))
// throw new Error("requestUpdated is not a REST Request")
// if (
// collectionsType.value.type !== "team-collections" ||
// !collectionsType.value.selectedTeam
// )
// throw new Error("Collections Type mismatch")
// modalLoadingState.value = true
// const data = {
// request: JSON.stringify(updatedRequest),
// title: updatedRequest.name,
// }
// platform.analytics?.logEvent({
// type: "HOPP_SAVE_REQUEST",
// createdNow: false,
// platform: "rest",
// workspaceType: "team",
// })
// pipe(
// updateTeamRequest(picked.value.requestID, data),
// TE.match(
// (err: GQLError<string>) => {
// toast.error(`${getErrorMessage(err)}`)
// modalLoadingState.value = false
// },
// () => {
// modalLoadingState.value = false
// requestSaved()
// }
// )
// )()
// } else if (picked.value.pickedType === "gql-my-request") {
// // TODO: Check for GQL request ?
// editGraphqlRequest(
// picked.value.folderPath,
// picked.value.requestIndex,
// updatedRequest as HoppGQLRequest
// )
// platform.analytics?.logEvent({
// type: "HOPP_SAVE_REQUEST",
// createdNow: false,
// platform: "gql",
// workspaceType: "team",
// })
// const { auth, headers } = cascadeParentCollectionForHeaderAuth(
// picked.value.folderPath,
// "graphql"
// )
// GQLTabs.currentActiveTab.value.document.inheritedProperties = {
// auth,
// headers,
// }
// requestSaved()
// } else if (picked.value.pickedType === "gql-my-folder") {
// // TODO: Check for GQL request ?
// saveGraphqlRequestAs(
// picked.value.folderPath,
// updatedRequest as HoppGQLRequest
// )
// platform.analytics?.logEvent({
// type: "HOPP_SAVE_REQUEST",
// createdNow: true,
// platform: "gql",
// workspaceType: "team",
// })
// const { auth, headers } = cascadeParentCollectionForHeaderAuth(
// picked.value.folderPath,
// "graphql"
// )
// GQLTabs.currentActiveTab.value.document.inheritedProperties = {
// auth,
// headers,
// }
// requestSaved()
// } else if (picked.value.pickedType === "gql-my-collection") {
// // TODO: Check for GQL request ?
// saveGraphqlRequestAs(
// `${picked.value.collectionIndex}`,
// updatedRequest as HoppGQLRequest
// )
// platform.analytics?.logEvent({
// type: "HOPP_SAVE_REQUEST",
// createdNow: true,
// platform: "gql",
// workspaceType: "team",
// })
// const { auth, headers } = cascadeParentCollectionForHeaderAuth(
// `${picked.value.collectionIndex}`,
// "graphql"
// )
// GQLTabs.currentActiveTab.value.document.inheritedProperties = {
// auth,
// headers,
// }
// requestSaved()
// }
} }
/** /**
@@ -478,50 +474,50 @@ const saveRequestAs = async () => {
* @param collectionID - ID of the collection or folder * @param collectionID - ID of the collection or folder
* @param requestUpdated - Updated request * @param requestUpdated - Updated request
*/ */
const updateTeamCollectionOrFolder = ( // const updateTeamCollectionOrFolder = (
collectionID: string, // collectionID: string,
requestUpdated: HoppRESTRequest // requestUpdated: HoppRESTRequest
) => { // ) => {
if ( // if (
collectionsType.value.type !== "team-collections" || // collectionsType.value.type !== "team-collections" ||
!collectionsType.value.selectedTeam // !collectionsType.value.selectedTeam
) // )
throw new Error("Collections Type mismatch") // throw new Error("Collections Type mismatch")
modalLoadingState.value = true // modalLoadingState.value = true
const data = { // const data = {
title: requestUpdated.name, // title: requestUpdated.name,
request: JSON.stringify(requestUpdated), // request: JSON.stringify(requestUpdated),
teamID: collectionsType.value.selectedTeam.id, // teamID: collectionsType.value.selectedTeam.id,
} // }
pipe( // pipe(
createRequestInCollection(collectionID, data), // createRequestInCollection(collectionID, data),
TE.match( // TE.match(
(err: GQLError<string>) => { // (err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`) // toast.error(`${getErrorMessage(err)}`)
modalLoadingState.value = false // modalLoadingState.value = false
}, // },
(result) => { // (result) => {
const { createRequestInCollection } = result // const { createRequestInCollection } = result
RESTTabs.currentActiveTab.value.document = { // RESTTabs.currentActiveTab.value.document = {
request: requestUpdated, // request: requestUpdated,
isDirty: false, // isDirty: false,
saveContext: { // saveContext: {
originLocation: "team-collection", // originLocation: "team-collection",
requestID: createRequestInCollection.id, // requestID: createRequestInCollection.id,
collectionID: createRequestInCollection.collection.id, // collectionID: createRequestInCollection.collection.id,
teamID: createRequestInCollection.collection.team.id, // teamID: createRequestInCollection.collection.team.id,
}, // },
} // }
modalLoadingState.value = false // modalLoadingState.value = false
requestSaved() // requestSaved()
} // }
) // )
)() // )()
} // }
const requestSaved = () => { const requestSaved = () => {
toast.success(`${t("request.added")}`) toast.success(`${t("request.added")}`)
@@ -536,24 +532,24 @@ const hideModal = () => {
emit("hide-modal") emit("hide-modal")
} }
const getErrorMessage = (err: GQLError<string>) => { // const getErrorMessage = (err: GQLError<string>) => {
console.error(err) // console.error(err)
if (err.type === "network_error") { // if (err.type === "network_error") {
return t("error.network_error") // return t("error.network_error")
} // }
switch (err.error) { // switch (err.error) {
case "team_coll/short_title": // case "team_coll/short_title":
return t("collection.name_length_insufficient") // return t("collection.name_length_insufficient")
case "team/invalid_coll_id": // case "team/invalid_coll_id":
return t("team.invalid_id") // return t("team.invalid_id")
case "team/not_required_role": // case "team/not_required_role":
return t("profile.no_permission") // return t("profile.no_permission")
case "team_req/not_required_role": // case "team_req/not_required_role":
return t("profile.no_permission") // return t("profile.no_permission")
case "Forbidden resource": // case "Forbidden resource":
return t("profile.no_permission") // return t("profile.no_permission")
default: // default:
return t("error.something_went_wrong") // return t("error.something_went_wrong")
} // }
} // }
</script> </script>

View File

@@ -387,7 +387,6 @@ import IconPlus from "~icons/lucide/plus"
import IconHelpCircle from "~icons/lucide/help-circle" import IconHelpCircle from "~icons/lucide/help-circle"
import IconImport from "~icons/lucide/folder-down" import IconImport from "~icons/lucide/folder-down"
import { computed, PropType, Ref, toRef } from "vue" import { computed, PropType, Ref, toRef } from "vue"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { useColorMode } from "@composables/theming" import { useColorMode } from "@composables/theming"
import { TeamCollection } from "~/helpers/teams/TeamCollection" import { TeamCollection } from "~/helpers/teams/TeamCollection"
@@ -400,17 +399,16 @@ import * as O from "fp-ts/Option"
import { Picked } from "~/helpers/types/HoppPicked.js" import { Picked } from "~/helpers/types/HoppPicked.js"
import { RESTTabService } from "~/services/tab/rest" import { RESTTabService } from "~/services/tab/rest"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { TeamWorkspace } from "~/services/workspace.service"
const t = useI18n() const t = useI18n()
const colorMode = useColorMode() const colorMode = useColorMode()
const tabs = useService(RESTTabService) const tabs = useService(RESTTabService)
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
type CollectionType = type CollectionType =
| { | {
type: "team-collections" type: "team-collections"
selectedTeam: SelectedTeam selectedTeam: TeamWorkspace
} }
| { type: "my-collections"; selectedTeam: undefined } | { type: "my-collections"; selectedTeam: undefined }
@@ -614,7 +612,7 @@ const hasNoTeamAccess = computed(
() => () =>
props.collectionsType.type === "team-collections" && props.collectionsType.type === "team-collections" &&
(props.collectionsType.selectedTeam === undefined || (props.collectionsType.selectedTeam === undefined ||
props.collectionsType.selectedTeam.myRole === "VIEWER") props.collectionsType.selectedTeam.role === "VIEWER")
) )
const isSelected = ({ const isSelected = ({

View File

@@ -21,7 +21,7 @@ import { GistSource } from "~/helpers/import-export/import/import-sources/GistSo
import IconFolderPlus from "~icons/lucide/folder-plus" import IconFolderPlus from "~icons/lucide/folder-plus"
import IconUser from "~icons/lucide/user" import IconUser from "~icons/lucide/user"
import { initializeDownloadCollection } from "~/helpers/import-export/export" import { initializeDownloadFile } from "~/helpers/import-export/export"
import { useReadonlyStream } from "~/composables/stream" import { useReadonlyStream } from "~/composables/stream"
import { platform } from "~/platform" import { platform } from "~/platform"
@@ -133,12 +133,12 @@ const GqlCollectionsHoppExporter: ImporterOrExporter = {
disabled: false, disabled: false,
applicableTo: ["personal-workspace", "team-workspace"], applicableTo: ["personal-workspace", "team-workspace"],
}, },
action: () => { action: async () => {
if (!gqlCollections.value.length) { if (!gqlCollections.value.length) {
return toast.error(t("error.no_collections_to_export")) return toast.error(t("error.no_collections_to_export"))
} }
const message = initializeDownloadCollection( const message = await initializeDownloadFile(
gqlCollectionsExporter(gqlCollections.value), gqlCollectionsExporter(gqlCollections.value),
"GQLCollections" "GQLCollections"
) )

View File

@@ -192,6 +192,7 @@ import { PersistenceService } from "~/services/persistence"
import { PersistedOAuthConfig } from "~/services/oauth/oauth.service" import { PersistedOAuthConfig } from "~/services/oauth/oauth.service"
import { GQLOptionTabs } from "~/components/graphql/RequestOptions.vue" import { GQLOptionTabs } from "~/components/graphql/RequestOptions.vue"
import { EditingProperties } from "../Properties.vue" import { EditingProperties } from "../Properties.vue"
import { defineActionHandler } from "~/helpers/actions"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
@@ -199,7 +200,7 @@ const toast = useToast()
defineProps<{ defineProps<{
// Whether to activate the ability to pick items (activates 'select' events) // Whether to activate the ability to pick items (activates 'select' events)
saveRequest: boolean saveRequest: boolean
picked: Picked picked: Picked | null
}>() }>()
const collections = useReadonlyStream(graphqlCollections$, [], "deep") const collections = useReadonlyStream(graphqlCollections$, [], "deep")
@@ -676,4 +677,11 @@ const resetSelectedData = () => {
editingRequest.value = null editingRequest.value = null
editingRequestIndex.value = null editingRequestIndex.value = null
} }
defineActionHandler("collection.new", () => {
displayModalAdd(true)
})
defineActionHandler("modals.collection.import", () => {
displayModalImportExport(true)
})
</script> </script>

View File

@@ -178,7 +178,6 @@ import { useI18n } from "@composables/i18n"
import { Picked } from "~/helpers/types/HoppPicked" import { Picked } from "~/helpers/types/HoppPicked"
import { useReadonlyStream } from "~/composables/stream" import { useReadonlyStream } from "~/composables/stream"
import { useLocalState } from "~/newstore/localstate" import { useLocalState } from "~/newstore/localstate"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { pipe } from "fp-ts/function" import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither" import * as TE from "fp-ts/TaskEither"
import { import {
@@ -245,7 +244,7 @@ import {
} from "~/helpers/collection/collection" } from "~/helpers/collection/collection"
import { currentReorderingStatus$ } from "~/newstore/reordering" import { currentReorderingStatus$ } from "~/newstore/reordering"
import { defineActionHandler, invokeAction } from "~/helpers/actions" import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { WorkspaceService } from "~/services/workspace.service" import { TeamWorkspace, WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { RESTTabService } from "~/services/tab/rest" import { RESTTabService } from "~/services/tab/rest"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
@@ -274,16 +273,14 @@ const props = defineProps({
const emit = defineEmits<{ const emit = defineEmits<{
(event: "select", payload: Picked | null): void (event: "select", payload: Picked | null): void
(event: "update-team", team: SelectedTeam): void (event: "update-team", team: TeamWorkspace): void
(event: "update-collection-type", type: CollectionType["type"]): void (event: "update-collection-type", type: CollectionType["type"]): void
}>() }>()
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
type CollectionType = type CollectionType =
| { | {
type: "team-collections" type: "team-collections"
selectedTeam: SelectedTeam selectedTeam: TeamWorkspace
} }
| { type: "my-collections"; selectedTeam: undefined } | { type: "my-collections"; selectedTeam: undefined }
@@ -330,9 +327,7 @@ const requestMoveLoading = ref<string[]>([])
// TeamList-Adapter // TeamList-Adapter
const workspaceService = useService(WorkspaceService) const workspaceService = useService(WorkspaceService)
const teamListAdapter = workspaceService.acquireTeamListAdapter(null) const teamListAdapter = workspaceService.acquireTeamListAdapter(null)
const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID") const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const teamListFetched = ref(false)
// Team Collection Adapter // Team Collection Adapter
const teamCollectionAdapter = new TeamCollectionAdapter(null) const teamCollectionAdapter = new TeamCollectionAdapter(null)
@@ -378,7 +373,7 @@ watch(
filterTexts, filterTexts,
(newFilterText) => { (newFilterText) => {
if (collectionsType.value.type === "team-collections") { if (collectionsType.value.type === "team-collections") {
const selectedTeamID = collectionsType.value.selectedTeam?.id const selectedTeamID = collectionsType.value.selectedTeam?.teamID
selectedTeamID && selectedTeamID &&
debouncedSearch(newFilterText, selectedTeamID)?.catch(() => {}) debouncedSearch(newFilterText, selectedTeamID)?.catch(() => {})
@@ -435,28 +430,6 @@ onMounted(() => {
} }
}) })
watch(
() => myTeams.value,
(newTeams) => {
if (newTeams && !teamListFetched.value) {
teamListFetched.value = true
if (REMEMBERED_TEAM_ID.value && currentUser.value) {
const team = newTeams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
if (team) updateSelectedTeam(team)
}
}
}
)
watch(
() => collectionsType.value.selectedTeam,
(newTeam) => {
if (newTeam) {
teamCollectionAdapter.changeTeamID(newTeam.id)
}
}
)
const switchToMyCollections = () => { const switchToMyCollections = () => {
collectionsType.value.type = "my-collections" collectionsType.value.type = "my-collections"
collectionsType.value.selectedTeam = undefined collectionsType.value.selectedTeam = undefined
@@ -488,11 +461,12 @@ const expandTeamCollection = (collectionID: string) => {
teamCollectionAdapter.expandCollection(collectionID) teamCollectionAdapter.expandCollection(collectionID)
} }
const updateSelectedTeam = (team: SelectedTeam) => { const updateSelectedTeam = (team: TeamWorkspace) => {
if (team) { if (team) {
collectionsType.value.type = "team-collections" collectionsType.value.type = "team-collections"
teamCollectionAdapter.changeTeamID(team.teamID)
collectionsType.value.selectedTeam = team collectionsType.value.selectedTeam = team
REMEMBERED_TEAM_ID.value = team.id REMEMBERED_TEAM_ID.value = team.teamID
emit("update-team", team) emit("update-team", team)
emit("update-collection-type", "team-collections") emit("update-collection-type", "team-collections")
} }
@@ -501,23 +475,14 @@ const updateSelectedTeam = (team: SelectedTeam) => {
const workspace = workspaceService.currentWorkspace const workspace = workspaceService.currentWorkspace
// Used to switch collection type and team when user switch workspace in the global workspace switcher // Used to switch collection type and team when user switch workspace in the global workspace switcher
// Check if there is a teamID in the workspace, if yes, switch to team collections and select the team
// If there is no teamID, switch to my collections
watch( watch(
() => { workspace,
const space = workspace.value (newWorkspace) => {
return space.type === "personal" ? undefined : space.teamID if (newWorkspace.type === "personal") {
}, switchToMyCollections()
(teamID) => { } else if (newWorkspace.type === "team") {
if (teamID) { updateSelectedTeam(newWorkspace)
const team = myTeams.value?.find((t) => t.id === teamID)
if (team) {
updateSelectedTeam(team)
}
return
} }
return switchToMyCollections()
}, },
{ {
immediate: true, immediate: true,
@@ -545,7 +510,7 @@ const hasTeamWriteAccess = computed(() => {
return false return false
} }
const role = collectionsType.value.selectedTeam?.myRole const role = collectionsType.value.selectedTeam?.role
return role === "OWNER" || role === "EDITOR" return role === "OWNER" || role === "EDITOR"
}) })
@@ -760,7 +725,7 @@ const addNewRootCollection = (name: string) => {
}) })
pipe( pipe(
createNewRootCollection(name, collectionsType.value.selectedTeam.id), createNewRootCollection(name, collectionsType.value.selectedTeam.teamID),
TE.match( TE.match(
(err: GQLError<string>) => { (err: GQLError<string>) => {
toast.error(`${getErrorMessage(err)}`) toast.error(`${getErrorMessage(err)}`)
@@ -831,7 +796,7 @@ const onAddRequest = (requestName: string) => {
const data = { const data = {
request: JSON.stringify(newRequest), request: JSON.stringify(newRequest),
teamID: collectionsType.value.selectedTeam.id, teamID: collectionsType.value.selectedTeam.teamID,
title: requestName, title: requestName,
} }
@@ -1158,7 +1123,7 @@ const duplicateRequest = (payload: {
const data = { const data = {
request: JSON.stringify(newRequest), request: JSON.stringify(newRequest),
teamID: collectionsType.value.selectedTeam.id, teamID: collectionsType.value.selectedTeam.teamID,
title: `${request.name} - ${t("action.duplicate")}`, title: `${request.name} - ${t("action.duplicate")}`,
} }
@@ -2351,4 +2316,7 @@ const getErrorMessage = (err: GQLError<string>) => {
defineActionHandler("collection.new", () => { defineActionHandler("collection.new", () => {
displayModalAdd(true) displayModalAdd(true)
}) })
defineActionHandler("modals.collection.import", () => {
displayModalImportExport(true)
})
</script> </script>

View File

@@ -37,7 +37,7 @@ import IconFolderPlus from "~icons/lucide/folder-plus"
import IconPostman from "~icons/hopp/postman" import IconPostman from "~icons/hopp/postman"
import IconInsomnia from "~icons/hopp/insomnia" import IconInsomnia from "~icons/hopp/insomnia"
import IconUser from "~icons/lucide/user" import IconUser from "~icons/lucide/user"
import { initializeDownloadCollection } from "~/helpers/import-export/export" import { initializeDownloadFile } from "~/helpers/import-export/export"
import { computed } from "vue" import { computed } from "vue"
import { useReadonlyStream } from "~/composables/stream" import { useReadonlyStream } from "~/composables/stream"
import { environmentsExporter } from "~/helpers/import-export/export/environments" import { environmentsExporter } from "~/helpers/import-export/export/environments"
@@ -230,12 +230,12 @@ const HoppEnvironmentsExport: ImporterOrExporter = {
disabled: false, disabled: false,
applicableTo: ["personal-workspace", "team-workspace"], applicableTo: ["personal-workspace", "team-workspace"],
}, },
action: () => { action: async () => {
if (!environmentJson.value.length) { if (!environmentJson.value.length) {
return toast.error(t("error.no_environments_to_export")) return toast.error(t("error.no_environments_to_export"))
} }
const message = initializeDownloadCollection( const message = await initializeDownloadFile(
environmentsExporter(environmentJson.value), environmentsExporter(environmentJson.value),
"Environments" "Environments"
) )

View File

@@ -364,6 +364,7 @@ const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
teamID: team.id, teamID: team.id,
teamName: team.name, teamName: team.name,
type: "team", type: "team",
role: team.myRole,
}) })
} }
watch( watch(

View File

@@ -46,41 +46,38 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from "vue"
import { isEqual } from "lodash-es"
import { platform } from "~/platform"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { useReadonlyStream, useStream } from "@composables/stream" import { useReadonlyStream, useStream } from "@composables/stream"
import { Environment } from "@hoppscotch/data"
import { useService } from "dioc/vue"
import * as TE from "fp-ts/TaskEither"
import { pipe } from "fp-ts/function"
import { isEqual } from "lodash-es"
import { computed, ref, watch } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast"
import { defineActionHandler } from "~/helpers/actions"
import { GQLError } from "~/helpers/backend/GQLClient"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter"
import { import {
deleteEnvironment,
getSelectedEnvironmentIndex, getSelectedEnvironmentIndex,
globalEnv$, globalEnv$,
selectedEnvironmentIndex$, selectedEnvironmentIndex$,
setSelectedEnvironmentIndex, setSelectedEnvironmentIndex,
} from "~/newstore/environments" } from "~/newstore/environments"
import TeamEnvironmentAdapter from "~/helpers/teams/TeamEnvironmentAdapter"
import { defineActionHandler } from "~/helpers/actions"
import { useLocalState } from "~/newstore/localstate" import { useLocalState } from "~/newstore/localstate"
import { pipe } from "fp-ts/function" import { platform } from "~/platform"
import * as TE from "fp-ts/TaskEither" import { TeamWorkspace, WorkspaceService } from "~/services/workspace.service"
import { GQLError } from "~/helpers/backend/GQLClient"
import { deleteEnvironment } from "~/newstore/environments"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import { useToast } from "~/composables/toast"
import { WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue"
import { Environment } from "@hoppscotch/data"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
type EnvironmentType = "my-environments" | "team-environments" type EnvironmentType = "my-environments" | "team-environments"
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
type EnvironmentsChooseType = { type EnvironmentsChooseType = {
type: EnvironmentType type: EnvironmentType
selectedTeam: SelectedTeam selectedTeam: TeamWorkspace | undefined
} }
const environmentType = ref<EnvironmentsChooseType>({ const environmentType = ref<EnvironmentsChooseType>({
@@ -102,11 +99,7 @@ const currentUser = useReadonlyStream(
platform.auth.getCurrentUser() platform.auth.getCurrentUser()
) )
// TeamList-Adapter
const workspaceService = useService(WorkspaceService) const workspaceService = useService(WorkspaceService)
const teamListAdapter = workspaceService.acquireTeamListAdapter(null)
const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
const teamListFetched = ref(false)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID") const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const adapter = new TeamEnvironmentAdapter(undefined) const adapter = new TeamEnvironmentAdapter(undefined)
@@ -118,29 +111,17 @@ const loading = computed(
() => adapterLoading.value && teamEnvironmentList.value.length === 0 () => adapterLoading.value && teamEnvironmentList.value.length === 0
) )
watch(
() => myTeams.value,
(newTeams) => {
if (newTeams && !teamListFetched.value) {
teamListFetched.value = true
if (REMEMBERED_TEAM_ID.value && currentUser.value) {
const team = newTeams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
if (team) updateSelectedTeam(team)
}
}
}
)
const switchToMyEnvironments = () => { const switchToMyEnvironments = () => {
environmentType.value.selectedTeam = undefined environmentType.value.selectedTeam = undefined
updateEnvironmentType("my-environments") updateEnvironmentType("my-environments")
adapter.changeTeamID(undefined) adapter.changeTeamID(undefined)
} }
const updateSelectedTeam = (newSelectedTeam: SelectedTeam | undefined) => { const updateSelectedTeam = (newSelectedTeam: TeamWorkspace | undefined) => {
if (newSelectedTeam) { if (newSelectedTeam) {
adapter.changeTeamID(newSelectedTeam.teamID)
environmentType.value.selectedTeam = newSelectedTeam environmentType.value.selectedTeam = newSelectedTeam
REMEMBERED_TEAM_ID.value = newSelectedTeam.id REMEMBERED_TEAM_ID.value = newSelectedTeam.teamID
updateEnvironmentType("team-environments") updateEnvironmentType("team-environments")
} }
} }
@@ -148,15 +129,6 @@ const updateEnvironmentType = (newEnvironmentType: EnvironmentType) => {
environmentType.value.type = newEnvironmentType environmentType.value.type = newEnvironmentType
} }
watch(
() => environmentType.value.selectedTeam,
(newTeam) => {
if (newTeam) {
adapter.changeTeamID(newTeam.id)
}
}
)
const workspace = workspaceService.currentWorkspace const workspace = workspaceService.currentWorkspace
// Switch to my environments if workspace is personal and to team environments if workspace is team // Switch to my environments if workspace is personal and to team environments if workspace is team
@@ -170,8 +142,7 @@ watch(workspace, (newWorkspace) => {
}) })
} }
} else if (newWorkspace.type === "team") { } else if (newWorkspace.type === "team") {
const team = myTeams.value?.find((t) => t.id === newWorkspace.teamID) updateSelectedTeam(newWorkspace)
updateSelectedTeam(team)
} }
}) })

View File

@@ -54,9 +54,7 @@
:key="tab.id" :key="tab.id"
:label="tab.label" :label="tab.label"
> >
<div <div class="divide-y divide-dividerLight">
class="divide-y divide-dividerLight rounded border border-divider"
>
<HoppSmartPlaceholder <HoppSmartPlaceholder
v-if="tab.variables.length === 0" v-if="tab.variables.length === 0"
:src="`/images/states/${colorMode.value}/blockchain.svg`" :src="`/images/states/${colorMode.value}/blockchain.svg`"

View File

@@ -56,9 +56,7 @@
:key="tab.id" :key="tab.id"
:label="tab.label" :label="tab.label"
> >
<div <div class="divide-y divide-dividerLight">
class="divide-y divide-dividerLight rounded border border-divider"
>
<HoppSmartPlaceholder <HoppSmartPlaceholder
v-if="tab.variables.length === 0" v-if="tab.variables.length === 0"
:src="`/images/states/${colorMode.value}/blockchain.svg`" :src="`/images/states/${colorMode.value}/blockchain.svg`"

View File

@@ -4,7 +4,7 @@
class="sticky top-upperPrimaryStickyFold z-10 flex flex-1 flex-shrink-0 justify-between overflow-x-auto border-b border-dividerLight bg-primary" class="sticky top-upperPrimaryStickyFold z-10 flex flex-1 flex-shrink-0 justify-between overflow-x-auto border-b border-dividerLight bg-primary"
> >
<HoppButtonSecondary <HoppButtonSecondary
v-if="team === undefined || team.myRole === 'VIEWER'" v-if="team === undefined || team.role === 'VIEWER'"
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
disabled disabled
class="!rounded-none" class="!rounded-none"
@@ -28,7 +28,7 @@
:icon="IconHelpCircle" :icon="IconHelpCircle"
/> />
<HoppButtonSecondary <HoppButtonSecondary
v-if="team !== undefined && team.myRole === 'VIEWER'" v-if="team !== undefined && team.role === 'VIEWER'"
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
disabled disabled
:icon="IconImport" :icon="IconImport"
@@ -84,7 +84,7 @@
)" )"
:key="`environment-${index}`" :key="`environment-${index}`"
:environment="environment" :environment="environment"
:is-viewer="team?.myRole === 'VIEWER'" :is-viewer="team?.role === 'VIEWER'"
@edit-environment="editEnvironment(environment)" @edit-environment="editEnvironment(environment)"
/> />
</div> </div>
@@ -103,16 +103,16 @@
:show="showModalDetails" :show="showModalDetails"
:action="action" :action="action"
:editing-environment="editingEnvironment" :editing-environment="editingEnvironment"
:editing-team-id="team?.id" :editing-team-id="team?.teamID"
:editing-variable-name="editingVariableName" :editing-variable-name="editingVariableName"
:is-secret-option-selected="secretOptionSelected" :is-secret-option-selected="secretOptionSelected"
:is-viewer="team?.myRole === 'VIEWER'" :is-viewer="team?.role === 'VIEWER'"
@hide-modal="displayModalEdit(false)" @hide-modal="displayModalEdit(false)"
/> />
<EnvironmentsImportExport <EnvironmentsImportExport
v-if="showModalImportExport" v-if="showModalImportExport"
:team-environments="teamEnvironments" :team-environments="teamEnvironments"
:team-id="team?.id" :team-id="team?.teamID"
environment-type="TEAM_ENV" environment-type="TEAM_ENV"
@hide-modal="displayModalImportExport(false)" @hide-modal="displayModalImportExport(false)"
/> />
@@ -129,16 +129,14 @@ import IconPlus from "~icons/lucide/plus"
import IconHelpCircle from "~icons/lucide/help-circle" import IconHelpCircle from "~icons/lucide/help-circle"
import IconImport from "~icons/lucide/folder-down" import IconImport from "~icons/lucide/folder-down"
import { defineActionHandler } from "~/helpers/actions" import { defineActionHandler } from "~/helpers/actions"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql" import { TeamWorkspace } from "~/services/workspace.service"
const t = useI18n() const t = useI18n()
const colorMode = useColorMode() const colorMode = useColorMode()
type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
const props = defineProps<{ const props = defineProps<{
team: SelectedTeam team: TeamWorkspace | undefined
teamEnvironments: TeamEnvironment[] teamEnvironments: TeamEnvironment[]
adapterError: GQLError<string> | null adapterError: GQLError<string> | null
loading: boolean loading: boolean
@@ -151,7 +149,7 @@ const editingEnvironment = ref<TeamEnvironment | null>(null)
const editingVariableName = ref("") const editingVariableName = ref("")
const secretOptionSelected = ref(false) const secretOptionSelected = ref(false)
const isTeamViewer = computed(() => props.team?.myRole === "VIEWER") const isTeamViewer = computed(() => props.team?.role === "VIEWER")
const displayModalAdd = (shouldDisplay: boolean) => { const displayModalAdd = (shouldDisplay: boolean) => {
action.value = "new" action.value = "new"

View File

@@ -595,7 +595,7 @@ const getComputedAuthHeaders = (
} else if (request.auth.authType === "api-key") { } else if (request.auth.authType === "api-key") {
const { key, addTo } = request.auth const { key, addTo } = request.auth
if (addTo === "Headers" && key) { if (addTo === "HEADERS" && key) {
headers.push({ headers.push({
active: true, active: true,
key, key,

View File

@@ -10,7 +10,7 @@
autocomplete="off" autocomplete="off"
spellcheck="false" spellcheck="false"
class="w-full rounded border border-divider bg-primaryLight px-4 py-2 text-secondaryDark" class="w-full rounded border border-divider bg-primaryLight px-4 py-2 text-secondaryDark"
:placeholder="`${t('request.url')}`" :placeholder="`${t('graphql.url_placeholder')}`"
:disabled="connected" :disabled="connected"
@keyup.enter="onConnectClick" @keyup.enter="onConnectClick"
/> />

View File

@@ -54,7 +54,7 @@
> >
<SmartEnvInput <SmartEnvInput
v-model="tab.document.request.endpoint" v-model="tab.document.request.endpoint"
:placeholder="`${t('request.url')}`" :placeholder="`${t('request.url_placeholder')}`"
:auto-complete-source="userHistories" :auto-complete-source="userHistories"
:auto-complete-env="true" :auto-complete-env="true"
:inspection-results="tabResults" :inspection-results="tabResults"
@@ -236,16 +236,28 @@ import { useI18n } from "@composables/i18n"
import { useSetting } from "@composables/settings" import { useSetting } from "@composables/settings"
import { useReadonlyStream, useStreamSubscriber } from "@composables/stream" import { useReadonlyStream, useStreamSubscriber } from "@composables/stream"
import { useToast } from "@composables/toast" import { useToast } from "@composables/toast"
import { HoppRESTRequest } from "@hoppscotch/data"
import { useVModel } from "@vueuse/core" import { useVModel } from "@vueuse/core"
import { useService } from "dioc/vue"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { Ref, computed, ref, onUnmounted } from "vue" import { Ref, computed, onUnmounted, ref } from "vue"
import { runRESTRequest$ } from "~/helpers/RequestRunner"
import { defineActionHandler, invokeAction } from "~/helpers/actions" import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { runMutation } from "~/helpers/backend/GQLClient" import { runMutation } from "~/helpers/backend/GQLClient"
import { UpdateRequestDocument } from "~/helpers/backend/graphql" import { UpdateRequestDocument } from "~/helpers/backend/graphql"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils" import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
import { runRESTRequest$ } from "~/helpers/RequestRunner" import { getDefaultRESTRequest } from "~/helpers/rest/default"
import { HoppRESTDocument } from "~/helpers/rest/document"
import { getMethodLabelColor } from "~/helpers/rest/labelColoring"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse" import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { editRESTRequest } from "~/newstore/collections" import { RESTHistoryEntry, restHistory$ } from "~/newstore/history"
import { platform } from "~/platform"
import { InspectionService } from "~/services/inspection"
import { InterceptorService } from "~/services/interceptor.service"
import { NewWorkspaceService } from "~/services/new-workspace"
import { HoppTab } from "~/services/tab"
import { RESTTabService } from "~/services/tab/rest"
import { WorkspaceService } from "~/services/workspace.service"
import IconChevronDown from "~icons/lucide/chevron-down" import IconChevronDown from "~icons/lucide/chevron-down"
import IconCode2 from "~icons/lucide/code-2" import IconCode2 from "~icons/lucide/code-2"
import IconFileCode from "~icons/lucide/file-code" import IconFileCode from "~icons/lucide/file-code"
@@ -253,21 +265,10 @@ import IconFolderPlus from "~icons/lucide/folder-plus"
import IconRotateCCW from "~icons/lucide/rotate-ccw" import IconRotateCCW from "~icons/lucide/rotate-ccw"
import IconSave from "~icons/lucide/save" import IconSave from "~icons/lucide/save"
import IconShare2 from "~icons/lucide/share-2" import IconShare2 from "~icons/lucide/share-2"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
import { RESTHistoryEntry, restHistory$ } from "~/newstore/history"
import { platform } from "~/platform"
import { HoppRESTRequest } from "@hoppscotch/data"
import { useService } from "dioc/vue"
import { InspectionService } from "~/services/inspection"
import { InterceptorService } from "~/services/interceptor.service"
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
import { RESTTabService } from "~/services/tab/rest"
import { getMethodLabelColor } from "~/helpers/rest/labelColoring"
import { WorkspaceService } from "~/services/workspace.service"
const t = useI18n() const t = useI18n()
const interceptorService = useService(InterceptorService) const interceptorService = useService(InterceptorService)
const newWorkspaceService = useService(NewWorkspaceService)
const methods = [ const methods = [
"GET", "GET",
@@ -506,34 +507,61 @@ const cycleDownMethod = () => {
} }
} }
const saveRequest = () => { const saveRequest = async () => {
const saveCtx = tab.value.document.saveContext const { saveContext } = tab.value.document
if (!saveCtx) { if (!saveContext) {
showSaveRequestModal.value = true showSaveRequestModal.value = true
return return
} }
if (saveCtx.originLocation === "user-collection") {
const req = tab.value.document.request
try { if (saveContext.originLocation === "workspace-user-collection") {
editRESTRequest(saveCtx.folderPath, saveCtx.requestIndex, req) const updatedRequest = tab.value.document.request
tab.value.document.isDirty = false if (
!newWorkspaceService.activeWorkspaceHandle.value ||
platform.analytics?.logEvent({ !saveContext.requestHandle
type: "HOPP_SAVE_REQUEST", ) {
platform: "rest", return
createdNow: false,
workspaceType: "personal",
})
toast.success(`${t("request.saved")}`)
} catch (e) {
tab.value.document.saveContext = undefined
saveRequest()
} }
} else if (saveCtx.originLocation === "team-collection") {
const { requestHandle } = saveContext
const requestHandleRef = requestHandle.get()
if (!requestHandleRef.value) {
return
}
if (requestHandleRef.value.type === "invalid") {
showSaveRequestModal.value = true
return
}
const updateRequestResult = await newWorkspaceService.updateRESTRequest(
requestHandle,
updatedRequest
)
if (E.isLeft(updateRequestResult)) {
// INVALID_REQUEST_HANDLE
showSaveRequestModal.value = true
if (!tab.value.document.isDirty) {
tab.value.document.isDirty = true
}
return
}
tab.value.document.isDirty = false
tab.value.document.saveContext = {
...saveContext,
requestHandle,
}
toast.success(`${t("request.saved")}`)
} else if (saveContext.originLocation === "team-collection") {
const req = tab.value.document.request const req = tab.value.document.request
// TODO: handle error case (NOTE: overwriteRequestTeams is async) // TODO: handle error case (NOTE: overwriteRequestTeams is async)
@@ -546,7 +574,7 @@ const saveRequest = () => {
}) })
runMutation(UpdateRequestDocument, { runMutation(UpdateRequestDocument, {
requestID: saveCtx.requestID, requestID: saveContext.requestID,
data: { data: {
title: req.name, title: req.name,
request: JSON.stringify(req), request: JSON.stringify(req),

View File

@@ -17,8 +17,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { watch } from "vue" import { watch } from "vue"
import { useVModel } from "@vueuse/core" import { useVModel } from "@vueuse/core"
import { cloneDeep } from "lodash-es" import { cloneDeep, isEqual } from "lodash-es"
import { isEqualHoppRESTRequest } from "@hoppscotch/data"
import { HoppTab } from "~/services/tab" import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document" import { HoppRESTDocument } from "~/helpers/rest/document"
@@ -32,15 +31,42 @@ const emit = defineEmits<{
const tab = useVModel(props, "modelValue", emit) const tab = useVModel(props, "modelValue", emit)
// TODO: Come up with a better dirty check
let oldRequest = cloneDeep(tab.value.document.request) let oldRequest = cloneDeep(tab.value.document.request)
watch( watch(
() => tab.value.document.request, () => tab.value.document.request,
(updatedValue) => { (updatedValue) => {
// Request from the collection tree
if ( if (
!tab.value.document.isDirty && tab.value.document.saveContext?.originLocation ===
!isEqualHoppRESTRequest(oldRequest, updatedValue) "workspace-user-collection"
) { ) {
const requestHandleRef =
tab.value.document.saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return
}
if (
!tab.value.document.isDirty &&
!isEqual(oldRequest, requestHandleRef?.value.data.request)
) {
tab.value.document.isDirty = true
}
if (
tab.value.document.isDirty &&
isEqual(oldRequest, requestHandleRef?.value.data.request)
) {
tab.value.document.isDirty = false
}
return
}
// Unsaved request
if (!tab.value.document.isDirty && !isEqual(oldRequest, updatedValue)) {
tab.value.document.isDirty = true tab.value.document.isDirty = true
} }

View File

@@ -10,7 +10,8 @@
:icon="IconFolder" :icon="IconFolder"
:label="`${t('tab.collections')}`" :label="`${t('tab.collections')}`"
> >
<Collections /> <!-- <Collections /> -->
<NewCollections :platform="'rest'" />
</HoppSmartTab> </HoppSmartTab>
<HoppSmartTab <HoppSmartTab
:id="'env'" :id="'env'"
@@ -37,12 +38,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import IconClock from "~icons/lucide/clock"
import IconLayers from "~icons/lucide/layers"
import IconFolder from "~icons/lucide/folder"
import IconShare2 from "~icons/lucide/share-2"
import { ref } from "vue"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { ref } from "vue"
import IconClock from "~icons/lucide/clock"
import IconFolder from "~icons/lucide/folder"
import IconLayers from "~icons/lucide/layers"
import IconShare2 from "~icons/lucide/share-2"
const t = useI18n() const t = useI18n()

View File

@@ -106,15 +106,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue" import { ref } from "vue"
import { TippyComponent } from "vue-tippy" import { TippyComponent } from "vue-tippy"
import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { HoppRESTDocument } from "~/helpers/rest/document"
import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring"
import { HoppTab } from "~/services/tab"
import IconCopy from "~icons/lucide/copy"
import IconFileEdit from "~icons/lucide/file-edit"
import IconShare2 from "~icons/lucide/share-2"
import IconXCircle from "~icons/lucide/x-circle" import IconXCircle from "~icons/lucide/x-circle"
import IconXSquare from "~icons/lucide/x-square" import IconXSquare from "~icons/lucide/x-square"
import IconFileEdit from "~icons/lucide/file-edit"
import IconCopy from "~icons/lucide/copy"
import IconShare2 from "~icons/lucide/share-2"
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
const t = useI18n() const t = useI18n()

View File

@@ -28,7 +28,13 @@
> >
<HoppSmartSelectWrapper> <HoppSmartSelectWrapper>
<HoppButtonSecondary <HoppButtonSecondary
:label="auth.addTo || t('state.none')" :label="
auth.addTo
? auth.addTo === 'HEADERS'
? t('authorization.pass_by_headers_label')
: t('authorization.pass_by_query_params_label')
: t('state.none')
"
class="ml-2 rounded-none pr-8" class="ml-2 rounded-none pr-8"
/> />
</HoppSmartSelectWrapper> </HoppSmartSelectWrapper>
@@ -40,23 +46,23 @@
@keyup.escape="hide()" @keyup.escape="hide()"
> >
<HoppSmartItem <HoppSmartItem
:icon="auth.addTo === 'Headers' ? IconCircleDot : IconCircle" :icon="auth.addTo === 'HEADERS' ? IconCircleDot : IconCircle"
:active="auth.addTo === 'Headers'" :active="auth.addTo === 'HEADERS'"
:label="'Headers'" :label="t('authorization.pass_by_headers_label')"
@click=" @click="
() => { () => {
auth.addTo = 'Headers' auth.addTo = 'HEADERS'
hide() hide()
} }
" "
/> />
<HoppSmartItem <HoppSmartItem
:icon="auth.addTo === 'Query params' ? IconCircleDot : IconCircle" :icon="auth.addTo === 'QUERY_PARAMS' ? IconCircleDot : IconCircle"
:active="auth.addTo === 'Query params'" :active="auth.addTo === 'QUERY_PARAMS'"
:label="'Query params'" :label="t('authorization.pass_by_query_params_label')"
@click=" @click="
() => { () => {
auth.addTo = 'Query params' auth.addTo = 'QUERY_PARAMS'
hide() hide()
} }
" "

View File

@@ -0,0 +1,32 @@
<template>
<div v-if="!activeWorkspaceHandle">No Workspace Selected.</div>
<NewCollectionsRest
v-else-if="platform === 'rest'"
:picked="picked"
:save-request="saveRequest"
:workspace-handle="activeWorkspaceHandle"
@select="(payload) => emit('select', payload)"
/>
</template>
<script setup lang="ts">
import { useService } from "dioc/vue"
import { Picked } from "~/helpers/types/HoppPicked"
import { NewWorkspaceService } from "~/services/new-workspace"
defineProps<{
picked?: Picked | null
platform: "rest" | "gql"
saveRequest?: boolean
}>()
const emit = defineEmits<{
(event: "select", payload: Picked | null): void
}>()
const workspaceService = useService(NewWorkspaceService)
const activeWorkspaceHandle = workspaceService.activeWorkspaceHandle
</script>

View File

@@ -0,0 +1,450 @@
<template>
<div class="flex flex-col">
<div
class="h-1 w-full transition"
:class="[
{
'bg-accentDark': isReorderable,
},
]"
@drop="orderUpdateCollectionEvent"
@dragover.prevent="ordering = true"
@dragleave="ordering = false"
@dragend="resetDragState"
></div>
<div class="relative flex flex-col">
<div
class="z-[1] pointer-events-none absolute inset-0 bg-accent opacity-0 transition"
:class="{
'opacity-25':
dragging && notSameDestination && notSameParentDestination,
}"
></div>
<div
class="z-[3] group pointer-events-auto relative flex cursor-pointer items-stretch"
:draggable="true"
@dragstart="dragStart"
@dragover="handleDragOver($event)"
@dragleave="resetDragState"
@dragend="
() => {
resetDragState()
dropItemID = ''
}
"
@drop="handleDrop($event)"
@contextmenu.prevent="options?.tippy?.show()"
>
<div
class="flex min-w-0 flex-1 items-center justify-center"
@click="emit('toggle-children')"
>
<span
class="pointer-events-none flex items-center justify-center px-4"
>
<component
:is="collectionIcon"
class="svg-icons"
:class="{ 'text-accent': isSelected }"
/>
</span>
<span
class="pointer-events-none flex min-w-0 flex-1 py-2 pr-2 transition group-hover:text-secondaryDark"
>
<span class="truncate" :class="{ 'text-accent': isSelected }">
{{ collectionView.name }}
</span>
</span>
</div>
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconFilePlus"
:title="t('request.new')"
class="hidden group-hover:inline-flex"
@click="addRequest"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconFolderPlus"
:title="t('folder.new')"
class="hidden group-hover:inline-flex"
@click="addChildCollection"
/>
<span>
<tippy
ref="options"
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions!.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.more')"
:icon="IconMoreVertical"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.r="requestAction?.$el.click()"
@keyup.n="folderAction?.$el.click()"
@keyup.e="edit?.$el.click()"
@keyup.delete="deleteAction?.$el.click()"
@keyup.x="exportAction?.$el.click()"
@keyup.p="propertiesAction?.$el.click()"
@keyup.escape="hide()"
>
<HoppSmartItem
ref="requestAction"
:icon="IconFilePlus"
:label="t('request.new')"
:shortcut="['R']"
@click="
() => {
addRequest()
hide()
}
"
/>
<HoppSmartItem
ref="folderAction"
:icon="IconFolderPlus"
:label="t('folder.new')"
:shortcut="['N']"
@click="
() => {
addChildCollection()
hide()
}
"
/>
<HoppSmartItem
ref="edit"
:icon="IconEdit"
:label="t('action.edit')"
:shortcut="['E']"
@click="
() => {
editCollection()
hide()
}
"
/>
<HoppSmartItem
ref="exportAction"
:icon="IconDownload"
:label="t('export.title')"
:shortcut="['X']"
@click="
() => {
emit('export-collection', collectionView.collectionID)
hide()
}
"
/>
<HoppSmartItem
ref="deleteAction"
:icon="IconTrash2"
:label="t('action.delete')"
:shortcut="['⌫']"
@click="
() => {
removeCollection()
hide()
}
"
/>
<HoppSmartItem
ref="propertiesAction"
:icon="IconSettings2"
:label="t('action.properties')"
:shortcut="['P']"
@click="
() => {
emit(
'edit-collection-properties',
collectionView.collectionID
)
hide()
}
"
/>
</div>
</template>
</tippy>
</span>
</div>
</div>
</div>
<div
v-if="collectionView.isLastItem"
class="w-full transition"
:class="[
{
'bg-accentDark': isLastItemReorderable,
'h-1 ': collectionView.isLastItem,
},
]"
@drop="updateLastItemOrder"
@dragover.prevent="orderingLastItem = true"
@dragleave="orderingLastItem = false"
@dragend="resetDragState"
></div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue"
import { TippyComponent } from "vue-tippy"
import { useI18n } from "~/composables/i18n"
import { useReadonlyStream } from "~/composables/stream"
import {
currentReorderingStatus$,
changeCurrentReorderStatus,
} from "~/newstore/reordering"
import { RESTCollectionViewCollection } from "~/services/new-workspace/view"
import IconCheckCircle from "~icons/lucide/check-circle"
import IconDownload from "~icons/lucide/download"
import IconEdit from "~icons/lucide/edit"
import IconFilePlus from "~icons/lucide/file-plus"
import IconFolder from "~icons/lucide/folder"
import IconFolderOpen from "~icons/lucide/folder-open"
import IconFolderPlus from "~icons/lucide/folder-plus"
import IconMoreVertical from "~icons/lucide/more-vertical"
import IconSettings2 from "~icons/lucide/settings-2"
import IconTrash2 from "~icons/lucide/trash-2"
const t = useI18n()
const props = defineProps<{
collectionView: RESTCollectionViewCollection
isOpen: boolean
isSelected?: boolean | null
}>()
const emit = defineEmits<{
(event: "add-child-collection", parentCollectionIndexPath: string): void
(event: "add-request", parentCollectionIndexPath: string): void
(event: "dragging", payload: boolean): void
(event: "drop-event", payload: DataTransfer): void
(event: "drag-event", payload: DataTransfer): void
(
event: "edit-child-collection",
payload: { collectionIndexPath: string; collectionName: string }
): void
(event: "edit-collection-properties", collectionIndexPath: string): void
(
event: "edit-root-collection",
payload: { collectionIndexPath: string; collectionName: string }
): void
(event: "export-collection", collectionIndexPath: string): void
(event: "remove-child-collection", collectionIndexPath: string): void
(event: "remove-root-collection", collectionIndexPath: string): void
(event: "toggle-children"): void
(event: "update-collection-order", payload: DataTransfer): void
(event: "update-last-collection-order", payload: DataTransfer): void
}>()
const tippyActions = ref<TippyComponent | null>(null)
const requestAction = ref<HTMLButtonElement | null>(null)
const folderAction = ref<HTMLButtonElement | null>(null)
const edit = ref<HTMLButtonElement | null>(null)
const deleteAction = ref<HTMLButtonElement | null>(null)
const exportAction = ref<HTMLButtonElement | null>(null)
const propertiesAction = ref<TippyComponent | null>(null)
const options = ref<TippyComponent | null>(null)
const dragging = ref(false)
const ordering = ref(false)
const orderingLastItem = ref(false)
const dropItemID = ref("")
const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
type: "collection",
id: "",
parentID: "",
})
// Used to determine if the collection is being dragged to a different destination
// This is used to make the highlight effect work
watch(
() => dragging.value,
(val) => {
if (val && notSameDestination.value && notSameParentDestination.value) {
emit("dragging", true)
} else {
emit("dragging", false)
}
}
)
const collectionIcon = computed(() => {
if (props.isSelected) {
return IconCheckCircle
}
return props.isOpen ? IconFolderOpen : IconFolder
})
const notSameParentDestination = computed(() => {
return (
currentReorderingStatus.value.parentID !== props.collectionView.collectionID
)
})
const isRequestDragging = computed(() => {
return currentReorderingStatus.value.type === "request"
})
const isSameParent = computed(() => {
return (
currentReorderingStatus.value.parentID ===
props.collectionView.parentCollectionID
)
})
const isReorderable = computed(() => {
return (
ordering.value &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value
)
})
const isLastItemReorderable = computed(() => {
return (
orderingLastItem.value &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value
)
})
const addChildCollection = () => {
emit("add-child-collection", props.collectionView.collectionID)
}
const addRequest = () => {
emit("add-request", props.collectionView.collectionID)
}
const editCollection = () => {
const { collectionID: collectionIndexPath, name: collectionName } =
props.collectionView
const data = {
collectionIndexPath,
collectionName,
}
collectionIndexPath.split("/").length > 1
? emit("edit-child-collection", data)
: emit("edit-root-collection", data)
}
const dragStart = ({ dataTransfer }: DragEvent) => {
if (dataTransfer) {
emit("drag-event", dataTransfer)
dropItemID.value = dataTransfer.getData("collectionIndex")
dragging.value = !dragging.value
changeCurrentReorderStatus({
type: "collection",
id: props.collectionView.collectionID,
parentID: props.collectionView.parentCollectionID,
})
}
}
// Trigger the re-ordering event when a collection is dragged over another collection's top section
const handleDragOver = (e: DragEvent) => {
dragging.value = true
if (
e.offsetY < 10 &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value
) {
ordering.value = true
dragging.value = false
orderingLastItem.value = false
} else if (
e.offsetY > 18 &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value &&
props.collectionView.isLastItem
) {
orderingLastItem.value = true
dragging.value = false
ordering.value = false
} else {
ordering.value = false
orderingLastItem.value = false
}
}
const handleDrop = (e: DragEvent) => {
if (ordering.value) {
orderUpdateCollectionEvent(e)
} else if (orderingLastItem.value) {
updateLastItemOrder(e)
} else {
notSameParentDestination.value ? dropEvent(e) : e.stopPropagation()
}
}
const dropEvent = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
emit("drop-event", e.dataTransfer)
resetDragState()
}
}
const orderUpdateCollectionEvent = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
emit("update-collection-order", e.dataTransfer)
resetDragState()
}
}
const updateLastItemOrder = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
emit("update-last-collection-order", e.dataTransfer)
resetDragState()
}
}
const notSameDestination = computed(() => {
return dropItemID.value !== props.collectionView.collectionID
})
const removeCollection = () => {
const { collectionID } = props.collectionView
collectionID.split("/").length > 1
? emit("remove-child-collection", collectionID)
: emit("remove-root-collection", collectionID)
}
const resetDragState = () => {
dragging.value = false
ordering.value = false
orderingLastItem.value = false
}
</script>

View File

@@ -0,0 +1,334 @@
<template>
<div class="flex flex-col">
<div
class="h-1 w-full transition"
:class="[
{
'bg-accentDark': isReorderable,
},
]"
@drop="updateRequestOrder"
@dragover.prevent="ordering = true"
@dragleave="resetDragState"
@dragend="resetDragState"
></div>
<div
class="group flex items-stretch"
:draggable="true"
@dragstart="dragStart"
@dragover="handleDragOver($event)"
@dragleave="resetDragState"
@dragend="resetDragState"
@drop="handleDrop"
@contextmenu.prevent="options?.tippy?.show()"
>
<div
class="pointer-events-auto flex min-w-0 flex-1 cursor-pointer items-center justify-center"
@click="selectRequest"
>
<span
class="pointer-events-none flex w-16 items-center justify-center truncate px-2"
:class="requestLabelColor"
:style="{ color: requestLabelColor }"
>
<component
:is="IconCheckCircle"
v-if="isSelected"
class="svg-icons"
:class="{ 'text-accent': isSelected }"
/>
<span v-else class="truncate text-tiny font-semibold">
{{ requestView.request.method }}
</span>
</span>
<span
class="pointer-events-none flex min-w-0 flex-1 items-center py-2 pr-2 transition group-hover:text-secondaryDark"
>
<span class="truncate" :class="{ 'text-accent': isSelected }">
{{ requestView.request.name }}
</span>
<span
v-if="props.isActive"
v-tippy="{ theme: 'tooltip' }"
class="relative mx-3 flex h-1.5 w-1.5 flex-shrink-0"
:title="`${t('collection.request_in_use')}`"
>
<span
class="absolute inline-flex h-full w-full flex-shrink-0 animate-ping rounded-full bg-green-500 opacity-75"
>
</span>
<span
class="relative inline-flex h-1.5 w-1.5 flex-shrink-0 rounded-full bg-green-500"
></span>
</span>
</span>
</div>
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconRotateCCW"
:title="t('action.restore')"
class="hidden group-hover:inline-flex"
@click="selectRequest"
/>
<span>
<tippy
ref="options"
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions?.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.more')"
:icon="IconMoreVertical"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.e="edit?.$el.click()"
@keyup.d="duplicate?.$el.click()"
@keyup.delete="deleteAction?.$el.click()"
@keyup.s="shareAction?.$el.click()"
@keyup.escape="hide()"
>
<HoppSmartItem
ref="edit"
:icon="IconEdit"
:label="t('action.edit')"
:shortcut="['E']"
@click="
() => {
emit('edit-request', {
requestIndexPath: requestView.requestID,
requestName: requestView.request.name,
})
hide()
}
"
/>
<HoppSmartItem
ref="duplicate"
:icon="IconCopy"
:label="t('action.duplicate')"
:shortcut="['D']"
@click="
() => {
emit('duplicate-request', requestView.requestID)
hide()
}
"
/>
<HoppSmartItem
ref="deleteAction"
:icon="IconTrash2"
:label="t('action.delete')"
:shortcut="['⌫']"
@click="
() => {
emit('remove-request', requestView.requestID)
hide()
}
"
/>
<HoppSmartItem
ref="shareAction"
:icon="IconShare2"
:label="t('action.share')"
:shortcut="['S']"
@click="
() => {
emit('share-request', requestView.request)
hide()
}
"
/>
</div>
</template>
</tippy>
</span>
</div>
</div>
<div
class="w-full transition"
:class="[
{
'bg-accentDark': isLastItemReorderable,
'h-1 ': props.requestView.isLastItem,
},
]"
@drop="handleDrop"
@dragover.prevent="orderingLastItem = true"
@dragleave="resetDragState"
@dragend="resetDragState"
></div>
</div>
</template>
<script setup lang="ts">
import { useI18n } from "@composables/i18n"
import { HoppRESTRequest } from "@hoppscotch/data"
import { computed, ref } from "vue"
import { TippyComponent } from "vue-tippy"
import { useReadonlyStream } from "~/composables/stream"
import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring"
import {
currentReorderingStatus$,
changeCurrentReorderStatus,
} from "~/newstore/reordering"
import { RESTCollectionViewRequest } from "~/services/new-workspace/view"
import IconCheckCircle from "~icons/lucide/check-circle"
import IconCopy from "~icons/lucide/copy"
import IconEdit from "~icons/lucide/edit"
import IconMoreVertical from "~icons/lucide/more-vertical"
import IconRotateCCW from "~icons/lucide/rotate-ccw"
import IconShare2 from "~icons/lucide/share-2"
import IconTrash2 from "~icons/lucide/trash-2"
const t = useI18n()
const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
type: "collection",
id: "",
parentID: "",
})
const props = defineProps<{
isActive: boolean
requestView: RESTCollectionViewRequest
isSelected: boolean | null | undefined
}>()
const emit = defineEmits<{
(event: "duplicate-request", requestIndexPath: string): void
(
event: "edit-request",
payload: {
requestIndexPath: string
requestName: string
}
): void
(event: "remove-request", requestIndexPath: string): void
(event: "select-request", requestIndexPath: string): void
(event: "share-request", request: HoppRESTRequest): void
(event: "drag-request", payload: DataTransfer): void
(event: "update-request-order", payload: DataTransfer): void
(event: "update-last-request-order", payload: DataTransfer): void
}>()
const tippyActions = ref<TippyComponent | null>(null)
const edit = ref<HTMLButtonElement | null>(null)
const deleteAction = ref<HTMLButtonElement | null>(null)
const options = ref<TippyComponent | null>(null)
const duplicate = ref<HTMLButtonElement | null>(null)
const shareAction = ref<HTMLButtonElement | null>(null)
const dragging = ref(false)
const ordering = ref(false)
const orderingLastItem = ref(false)
const isCollectionDragging = computed(() => {
return currentReorderingStatus.value.type === "collection"
})
const isLastItemReorderable = computed(() => {
return (
orderingLastItem.value && isSameParent.value && !isCollectionDragging.value
)
})
const isReorderable = computed(() => {
return (
ordering.value &&
!isCollectionDragging.value &&
isSameParent.value &&
!isSameRequest.value
)
})
const isSameParent = computed(() => {
return (
currentReorderingStatus.value.parentID === props.requestView.collectionID
)
})
const isSameRequest = computed(() => {
return currentReorderingStatus.value.id === props.requestView.requestID
})
const requestLabelColor = computed(() =>
getMethodLabelColorClassOf(props.requestView.request)
)
const dragStart = ({ dataTransfer }: DragEvent) => {
if (dataTransfer) {
emit("drag-request", dataTransfer)
dragging.value = !dragging.value
changeCurrentReorderStatus({
type: "request",
id: props.requestView.requestID,
parentID: props.requestView.collectionID,
})
}
}
const handleDrop = (e: DragEvent) => {
if (ordering.value) {
updateRequestOrder(e)
} else if (orderingLastItem.value) {
updateLastItemOrder(e)
} else {
updateRequestOrder(e)
}
}
// Trigger the re-ordering event when a request is dragged over another request's top section
const handleDragOver = (e: DragEvent) => {
dragging.value = true
if (e.offsetY < 10) {
ordering.value = true
dragging.value = false
orderingLastItem.value = false
} else if (e.offsetY > 18) {
orderingLastItem.value = true
dragging.value = false
ordering.value = false
} else {
ordering.value = false
orderingLastItem.value = false
}
}
const resetDragState = () => {
dragging.value = false
ordering.value = false
orderingLastItem.value = false
}
const selectRequest = () => emit("select-request", props.requestView.requestID)
const updateRequestOrder = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
resetDragState()
emit("update-request-order", e.dataTransfer)
}
}
const updateLastItemOrder = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
resetDragState()
emit("update-last-request-order", e.dataTransfer)
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -37,13 +37,17 @@ import { TeamNameCodec } from "~/helpers/backend/types/TeamName"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast" import { useToast } from "@composables/toast"
import { platform } from "~/platform" import { platform } from "~/platform"
import { useService } from "dioc/vue"
import { WorkspaceService } from "~/services/workspace.service"
import { useLocalState } from "~/newstore/localstate"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
defineProps<{ const props = defineProps<{
show: boolean show: boolean
switchWorkspaceAfterCreation?: boolean
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -52,8 +56,12 @@ const emit = defineEmits<{
const editingName = ref<string | null>(null) const editingName = ref<string | null>(null)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const isLoading = ref(false) const isLoading = ref(false)
const workspaceService = useService(WorkspaceService)
const addNewTeam = async () => { const addNewTeam = async () => {
isLoading.value = true isLoading.value = true
await pipe( await pipe(
@@ -76,8 +84,19 @@ const addNewTeam = async () => {
// Handle GQL errors (use err obj) // Handle GQL errors (use err obj)
} }
}, },
() => { (team) => {
toast.success(`${t("team.new_created")}`) toast.success(`${t("team.new_created")}`)
if (props.switchWorkspaceAfterCreation) {
REMEMBERED_TEAM_ID.value = team.id
workspaceService.changeWorkspace({
teamID: team.id,
teamName: team.name,
type: "team",
role: team.myRole,
})
}
hideModal() hideModal()
} }
) )

View File

@@ -3,7 +3,7 @@
class="flex items-center overflow-x-auto whitespace-nowrap border-b border-dividerLight px-4 py-2 text-tiny text-secondaryLight" class="flex items-center overflow-x-auto whitespace-nowrap border-b border-dividerLight px-4 py-2 text-tiny text-secondaryLight"
> >
<span class="truncate"> <span class="truncate">
{{ currentWorkspace }} {{ workspaceName ?? t("workspace.no_workspace") }}
</span> </span>
<icon-lucide-chevron-right v-if="section" class="mx-2" /> <icon-lucide-chevron-right v-if="section" class="mx-2" />
{{ section }} {{ section }}
@@ -14,29 +14,24 @@
import { computed } from "vue" import { computed } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { WorkspaceService } from "~/services/workspace.service" import { NewWorkspaceService } from "~/services/new-workspace"
const props = defineProps<{ defineProps<{
section?: string section?: string
isOnlyPersonal?: boolean
}>() }>()
const t = useI18n() const t = useI18n()
const workspaceService = useService(WorkspaceService) const workspaceService = useService(NewWorkspaceService)
const workspace = workspaceService.currentWorkspace const activeWorkspaceHandle = workspaceService.activeWorkspaceHandle
const currentWorkspace = computed(() => { const workspaceName = computed(() => {
if (props.isOnlyPersonal || workspace.value.type === "personal") { const activeWorkspaceHandleRef = activeWorkspaceHandle.value?.get()
return t("workspace.personal")
}
return teamWorkspaceName.value
})
const teamWorkspaceName = computed(() => { if (activeWorkspaceHandleRef?.value.type === "ok") {
if (workspace.value.type === "team" && workspace.value.teamName) { return activeWorkspaceHandleRef.value.data.name
return workspace.value.teamName
} }
return `${t("workspace.team")}`
return undefined
}) })
</script> </script>

View File

@@ -0,0 +1,54 @@
<template>
<div>
<div class="flex flex-col">
<HoppSmartItem
:label="'Personal Workspace'"
:info-icon="
activeWorkspaceInfo?.provider ===
personalWorkspaceProviderService.providerID &&
activeWorkspaceInfo.workspaceID === 'personal'
? IconCheck
: undefined
"
:active-info-icon="
activeWorkspaceInfo?.provider ===
personalWorkspaceProviderService.providerID &&
activeWorkspaceInfo.workspaceID === 'personal'
"
@click="selectWorkspace"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useService } from "dioc/vue"
import { NewWorkspaceService } from "~/services/new-workspace"
import { computed } from "vue"
import { PersonalWorkspaceProviderService } from "~/services/new-workspace/providers/personal.workspace"
import IconCheck from "~icons/lucide/check"
const workspaceService = useService(NewWorkspaceService)
const personalWorkspaceProviderService = useService(
PersonalWorkspaceProviderService
)
const activeWorkspaceInfo = computed(() => {
const activeWorkspaceHandleRef =
workspaceService.activeWorkspaceHandle.value?.get()
if (activeWorkspaceHandleRef?.value.type === "ok") {
return {
provider: activeWorkspaceHandleRef.value.data.providerID,
workspaceID: activeWorkspaceHandleRef.value.data.workspaceID,
}
}
return undefined
})
function selectWorkspace() {
workspaceService.activeWorkspaceHandle.value =
personalWorkspaceProviderService.getPersonalWorkspaceHandle()
}
</script>

View File

@@ -1,190 +1,36 @@
<template> <template>
<div ref="rootEl"> <div ref="rootEl">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="flex flex-col"> <div
<HoppSmartItem v-for="(selectorComponent, index) in workspaceSelectorComponents"
:label="t('workspace.personal')" :key="index"
:icon="IconUser" class="flex flex-col"
:info-icon="workspace.type === 'personal' ? IconDone : undefined" >
:active-info-icon="workspace.type === 'personal'" <component :is="selectorComponent" />
@click="switchToPersonalWorkspace"
/>
<hr /> <hr />
</div> </div>
<div v-if="loading" class="flex flex-col items-center justify-center p-4">
<HoppSmartSpinner class="mb-4" />
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
</div>
<HoppSmartPlaceholder
v-if="!loading && myTeams.length === 0"
:src="`/images/states/${colorMode.value}/add_group.svg`"
:alt="`${t('empty.teams')}`"
:text="`${t('empty.teams')}`"
>
<template #body>
<HoppButtonSecondary
:label="t('team.create_new')"
filled
outline
:icon="IconPlus"
@click="displayModalAdd(true)"
/>
</template>
</HoppSmartPlaceholder>
<div v-else-if="!loading" class="flex flex-col">
<div
class="sticky top-0 z-10 mb-2 flex items-center justify-between bg-popover py-2 pl-2"
>
<div class="flex items-center px-2 font-semibold text-secondaryLight">
{{ t("workspace.other_workspaces") }}
</div>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconPlus"
:title="`${t('team.create_new')}`"
outline
filled
class="ml-8 rounded !p-0.75"
@click="displayModalAdd(true)"
/>
</div>
<HoppSmartItem
v-for="(team, index) in myTeams"
:key="`team-${String(index)}`"
:icon="IconUsers"
:label="team.name"
:info-icon="isActiveWorkspace(team.id) ? IconDone : undefined"
:active-info-icon="isActiveWorkspace(team.id)"
@click="switchToTeamWorkspace(team)"
/>
</div>
<div
v-if="!loading && teamListAdapterError"
class="flex flex-col items-center py-4"
>
<icon-lucide-help-circle class="svg-icons mb-4" />
{{ t("error.something_went_wrong") }}
</div>
</div> </div>
<TeamsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from "vue"
import { useReadonlyStream } from "~/composables/stream"
import { platform } from "~/platform"
import { useI18n } from "@composables/i18n"
import IconUser from "~icons/lucide/user"
import IconUsers from "~icons/lucide/users"
import IconPlus from "~icons/lucide/plus"
import { useColorMode } from "@composables/theming"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import IconDone from "~icons/lucide/check"
import { useLocalState } from "~/newstore/localstate"
import { defineActionHandler } from "~/helpers/actions"
import { WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { useElementVisibility, useIntervalFn } from "@vueuse/core" import { NewWorkspaceService } from "~/services/new-workspace"
import { TestWorkspaceProviderService } from "~/services/new-workspace/providers/test.workspace"
const t = useI18n() useService(TestWorkspaceProviderService)
const colorMode = useColorMode()
const showModalAdd = ref(false) const newWorkspaceService = useService(NewWorkspaceService)
const workspaceSelectorComponents =
newWorkspaceService.workspaceSelectorComponents
const currentUser = useReadonlyStream( // TODO: Handle the updates to these actions
platform.auth.getProbableUserStream(), // defineActionHandler("modals.team.new", () => {
platform.auth.getProbableUser() // displayModalAdd(true)
) // })
//
const workspaceService = useService(WorkspaceService) // defineActionHandler("workspace.switch.personal", switchToPersonalWorkspace)
const teamListadapter = workspaceService.acquireTeamListAdapter(null) // defineActionHandler("workspace.switch", ({ teamId }) => {
const myTeams = useReadonlyStream(teamListadapter.teamList$, []) // const team = myTeams.value.find((t) => t.id === teamId)
const isTeamListLoading = useReadonlyStream(teamListadapter.loading$, false) // if (team) switchToTeamWorkspace(team)
const teamListAdapterError = useReadonlyStream(teamListadapter.error$, null) // })
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const teamListFetched = ref(false)
const rootEl = ref<HTMLElement>()
const elVisible = useElementVisibility(rootEl)
const { pause: pauseListPoll, resume: resumeListPoll } = useIntervalFn(() => {
if (teamListadapter.isInitialized) {
teamListadapter.fetchList()
}
}, 10000)
watch(
elVisible,
() => {
if (elVisible.value) {
teamListadapter.fetchList()
resumeListPoll()
} else {
pauseListPoll()
}
},
{ immediate: true }
)
watch(myTeams, (teams) => {
if (teams && !teamListFetched.value) {
teamListFetched.value = true
if (REMEMBERED_TEAM_ID.value && currentUser.value) {
const team = teams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
if (team) switchToTeamWorkspace(team)
}
}
})
const loading = computed(
() => isTeamListLoading.value && myTeams.value.length === 0
)
const workspace = workspaceService.currentWorkspace
const isActiveWorkspace = computed(() => (id: string) => {
if (workspace.value.type === "personal") return false
return workspace.value.teamID === id
})
const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
REMEMBERED_TEAM_ID.value = team.id
workspaceService.changeWorkspace({
teamID: team.id,
teamName: team.name,
type: "team",
})
}
const switchToPersonalWorkspace = () => {
REMEMBERED_TEAM_ID.value = undefined
workspaceService.changeWorkspace({
type: "personal",
})
}
watch(
() => currentUser.value,
(user) => {
if (!user) {
switchToPersonalWorkspace()
}
}
)
const displayModalAdd = (shouldDisplay: boolean) => {
showModalAdd.value = shouldDisplay
teamListadapter.fetchList()
}
defineActionHandler("modals.team.new", () => {
displayModalAdd(true)
})
defineActionHandler("workspace.switch.personal", switchToPersonalWorkspace)
defineActionHandler("workspace.switch", ({ teamId }) => {
const team = myTeams.value.find((t) => t.id === teamId)
if (team) switchToTeamWorkspace(team)
})
</script> </script>

View File

@@ -0,0 +1,65 @@
<template>
<div>
<div class="flex flex-col">
<HoppSmartItem
v-for="candidate in candidates"
:key="candidate.id"
:label="candidate.name"
:info-icon="
activeWorkspaceInfo?.provider ===
testWorkspaceProviderService.providerID &&
activeWorkspaceInfo.workspaceID === candidate.id
? IconCheck
: undefined
"
:active-info-icon="
activeWorkspaceInfo?.provider ===
testWorkspaceProviderService.providerID &&
activeWorkspaceInfo.workspaceID === candidate.id
"
@click="selectWorkspace(candidate.id)"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useService } from "dioc/vue"
import { computed } from "vue"
import { NewWorkspaceService } from "~/services/new-workspace"
import { TestWorkspaceProviderService } from "~/services/new-workspace/providers/test.workspace"
import IconCheck from "~icons/lucide/check"
import * as E from "fp-ts/Either"
const workspaceService = useService(NewWorkspaceService)
const testWorkspaceProviderService = useService(TestWorkspaceProviderService)
const candidates = testWorkspaceProviderService.getWorkspaceCandidates()
const activeWorkspaceInfo = computed(() => {
const activeWorkspaceHandle = workspaceService.activeWorkspaceHandle.value
const activeWorkspaceHandleRef = activeWorkspaceHandle?.get()
if (activeWorkspaceHandleRef?.value.type === "ok") {
return {
provider: activeWorkspaceHandleRef.value.data.providerID,
workspaceID: activeWorkspaceHandleRef.value.data.workspaceID,
}
}
return undefined
})
async function selectWorkspace(workspaceID: string) {
const result =
await testWorkspaceProviderService.getWorkspaceHandle(workspaceID)
// TODO: Re-evaluate this ?
if (E.isLeft(result)) {
console.error(result)
return
}
workspaceService.activeWorkspaceHandle.value = result.right
}
</script>

View File

@@ -36,6 +36,7 @@ export type HoppAction =
| "collection.new" // Create root collection | "collection.new" // Create root collection
| "flyouts.chat.open" // Shows the keybinds flyout | "flyouts.chat.open" // Shows the keybinds flyout
| "flyouts.keybinds.toggle" // Shows the keybinds flyout | "flyouts.keybinds.toggle" // Shows the keybinds flyout
| "modals.collection.import" // Shows the collection import modal
| "modals.search.toggle" // Shows the search modal | "modals.search.toggle" // Shows the search modal
| "modals.support.toggle" // Shows the support modal | "modals.support.toggle" // Shows the support modal
| "modals.share.toggle" // Shows the share modal | "modals.share.toggle" // Shows the share modal

View File

@@ -0,0 +1,92 @@
import { HoppCollection } from "@hoppscotch/data"
import { ChildrenResult, SmartTreeAdapter } from "@hoppscotch/ui"
import { Ref, computed, ref } from "vue"
import { navigateToFolderWithIndexPath } from "~/newstore/collections"
import { RESTCollectionViewItem } from "~/services/new-workspace/view"
export class WorkspaceRESTSearchCollectionTreeAdapter
implements SmartTreeAdapter<RESTCollectionViewItem>
{
constructor(public data: Ref<HoppCollection[]>) {}
getChildren(
nodeID: string | null
): Ref<ChildrenResult<RESTCollectionViewItem>> {
const result = ref<ChildrenResult<RESTCollectionViewItem>>({
status: "loading",
})
return computed(() => {
if (nodeID === null) {
result.value = {
status: "loaded",
data: this.data.value.map((item, index) => ({
id: index.toString(),
data: <RESTCollectionViewItem>{
type: "collection",
value: {
collectionID: index.toString(),
isLastItem: index === this.data.value.length - 1,
name: item.name,
parentCollectionID: null,
},
},
})),
}
} else {
const indexPath = nodeID.split("/").map((x) => parseInt(x))
const item = navigateToFolderWithIndexPath(this.data.value, indexPath)
if (item) {
const collections = item.folders.map(
(childCollection, childCollectionID) => {
return {
id: `${nodeID}/${childCollectionID}`,
data: <RESTCollectionViewItem>{
type: "collection",
value: {
isLastItem:
item.folders?.length > 1
? childCollectionID === item.folders.length - 1
: false,
collectionID: `${nodeID}/${childCollectionID}`,
name: childCollection.name,
parentCollectionID: nodeID,
},
},
}
}
)
const requests = item.requests.map((request, requestID) => {
return {
id: `${nodeID}/${requestID}`,
data: <RESTCollectionViewItem>{
type: "request",
value: {
isLastItem:
item.requests?.length > 1
? requestID === item.requests.length - 1
: false,
parentCollectionID: nodeID,
collectionID: nodeID,
requestID: `${nodeID}/${requestID}`,
request,
},
},
}
})
result.value = {
status: "loaded",
data: [...collections, ...requests],
}
}
}
return result.value
})
}
}

View File

@@ -0,0 +1,134 @@
import {
ChildrenResult,
SmartTreeAdapter,
} from "@hoppscotch/ui/dist/src/helpers/treeAdapter"
import * as E from "fp-ts/Either"
import { Ref, ref, watchEffect } from "vue"
import { NewWorkspaceService } from "~/services/new-workspace"
import { Handle } from "~/services/new-workspace/handle"
import { RESTCollectionViewItem } from "~/services/new-workspace/view"
import { Workspace } from "~/services/new-workspace/workspace"
export class WorkspaceRESTCollectionTreeAdapter
implements SmartTreeAdapter<RESTCollectionViewItem>
{
constructor(
private workspaceHandle: Handle<Workspace>,
private workspaceService: NewWorkspaceService
) {}
public getChildren(
nodeID: string | null,
nodeType?: string
): Ref<ChildrenResult<RESTCollectionViewItem>> {
const workspaceHandleRef = this.workspaceHandle.get()
if (workspaceHandleRef.value.type !== "ok") {
throw new Error("Cannot issue children with invalid workspace handle")
}
const result = ref<ChildrenResult<RESTCollectionViewItem>>({
status: "loading",
})
if (nodeID !== null) {
;(async () => {
if (nodeType === "request") {
result.value = {
status: "loaded",
data: [],
}
return
}
const collectionHandleResult =
await this.workspaceService.getCollectionHandle(
this.workspaceHandle,
nodeID
)
// TODO: Better error handling
if (E.isLeft(collectionHandleResult)) {
throw new Error(JSON.stringify(collectionHandleResult.left.error))
}
const collectionHandle = collectionHandleResult.right
const collectionChildrenResult =
await this.workspaceService.getRESTCollectionChildrenView(
collectionHandle
)
// TODO: Better error handling
if (E.isLeft(collectionChildrenResult)) {
throw new Error(JSON.stringify(collectionChildrenResult.left.error))
}
const collectionChildrenViewHandle =
collectionChildrenResult.right.get()
watchEffect(() => {
if (collectionChildrenViewHandle.value.type !== "ok") return
if (collectionChildrenViewHandle.value.data.loading.value) {
result.value = {
status: "loading",
}
} else {
result.value = {
status: "loaded",
data: collectionChildrenViewHandle.value.data.content.value.map(
(item) => ({
id:
item.type === "request"
? item.value.requestID
: item.value.collectionID,
data: item,
})
),
}
}
})
})()
} else {
;(async () => {
const viewResult =
await this.workspaceService.getRESTRootCollectionView(
this.workspaceHandle
)
// TODO: Better error handling
if (E.isLeft(viewResult)) {
throw new Error(JSON.stringify(viewResult.left.error))
}
const viewHandle = viewResult.right.get()
watchEffect(() => {
if (viewHandle.value.type !== "ok") return
if (viewHandle.value.data.loading.value) {
result.value = {
status: "loading",
}
} else {
result.value = {
status: "loaded",
data: viewHandle.value.data.collections.value.map((coll) => ({
id: coll.collectionID,
data: {
type: "collection",
value: coll,
},
})),
}
}
})
})()
}
return result
}
}

View File

@@ -1,12 +1,15 @@
import { HoppCollection } from "@hoppscotch/data" import { HoppCollection } from "@hoppscotch/data"
import { getAffectedIndexes } from "./affectedIndex"
import { GetSingleRequestDocument } from "../backend/graphql"
import { runGQLQuery } from "../backend/GQLClient"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { getService } from "~/modules/dioc" import { getService } from "~/modules/dioc"
import { RESTTabService } from "~/services/tab/rest"
import { HoppInheritedProperty } from "../types/HoppInheritedProperties"
import { GQLTabService } from "~/services/tab/graphql" import { GQLTabService } from "~/services/tab/graphql"
import { RESTTabService } from "~/services/tab/rest"
import { runGQLQuery } from "../backend/GQLClient"
import { GetSingleRequestDocument } from "../backend/graphql"
import { HoppGQLSaveContext } from "../graphql/document"
import { HoppRESTSaveContext } from "../rest/document"
import { HoppInheritedProperty } from "../types/HoppInheritedProperties"
import { getAffectedIndexes } from "./affectedIndex"
/** /**
* Resolve save context on reorder * Resolve save context on reorder
@@ -18,15 +21,12 @@ import { GQLTabService } from "~/services/tab/graphql"
* @returns * @returns
*/ */
export function resolveSaveContextOnCollectionReorder( export function resolveSaveContextOnCollectionReorder(payload: {
payload: { lastIndex: number
lastIndex: number newIndex: number
newIndex: number folderPath: string
folderPath: string length?: number // better way to do this? now it could be undefined
length?: number // better way to do this? now it could be undefined }) {
},
type: "remove" | "drop" = "remove"
) {
const { lastIndex, folderPath, length } = payload const { lastIndex, folderPath, length } = payload
let { newIndex } = payload let { newIndex } = payload
@@ -41,12 +41,6 @@ export function resolveSaveContextOnCollectionReorder(
if (newIndex === -1) { if (newIndex === -1) {
// if (newIndex === -1) remove it from the map because it will be deleted // if (newIndex === -1) remove it from the map because it will be deleted
affectedIndexes.delete(lastIndex) affectedIndexes.delete(lastIndex)
// when collection deleted opended requests from that collection be affected
if (type === "remove") {
resetSaveContextForAffectedRequests(
folderPath ? `${folderPath}/${lastIndex}` : lastIndex.toString()
)
}
} }
// add folder path as prefix to the affected indexes // add folder path as prefix to the affected indexes
@@ -62,10 +56,27 @@ export function resolveSaveContextOnCollectionReorder(
const tabService = getService(RESTTabService) const tabService = getService(RESTTabService)
const tabs = tabService.getTabsRefTo((tab) => { const tabs = tabService.getTabsRefTo((tab) => {
return ( if (tab.document.saveContext?.originLocation === "user-collection") {
tab.document.saveContext?.originLocation === "user-collection" && return affectedPaths.has(tab.document.saveContext.folderPath)
affectedPaths.has(tab.document.saveContext.folderPath) }
)
if (
tab.document.saveContext?.originLocation !== "workspace-user-collection"
) {
return false
}
const requestHandleRef = tab.document.saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return false
}
const { requestID } = requestHandleRef.value.data
const collectionID = requestID.split("/").slice(0, -1).join("/")
return affectedPaths.has(collectionID)
}) })
for (const tab of tabs) { for (const tab of tabs) {
@@ -75,6 +86,34 @@ export function resolveSaveContextOnCollectionReorder(
)! )!
tab.value.document.saveContext.folderPath = newPath tab.value.document.saveContext.folderPath = newPath
} }
if (
tab.value.document.saveContext?.originLocation !==
"workspace-user-collection"
) {
return false
}
const requestHandleRef = tab.value.document.saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return false
}
const { requestID } = requestHandleRef.value.data
const collectionID = requestID.split("/").slice(0, -1).join("/")
const newCollectionID = affectedPaths.get(collectionID)
const newRequestID = `${newCollectionID}/${
requestID.split("/").slice(-1)[0]
}`
requestHandleRef.value.data = {
...requestHandleRef.value.data,
collectionID: newCollectionID!,
requestID: newRequestID,
}
} }
} }
@@ -86,25 +125,63 @@ export function resolveSaveContextOnCollectionReorder(
*/ */
export function updateSaveContextForAffectedRequests( export function updateSaveContextForAffectedRequests(
oldFolderPath: string, draggedCollectionIndex: string,
newFolderPath: string destinationCollectionIndex: string
) { ) {
const tabService = getService(RESTTabService) const tabService = getService(RESTTabService)
const tabs = tabService.getTabsRefTo((tab) => {
return (
tab.document.saveContext?.originLocation === "user-collection" &&
tab.document.saveContext.folderPath.startsWith(oldFolderPath)
)
})
for (const tab of tabs) { const activeTabs = tabService.getActiveTabs()
if (tab.value.document.saveContext?.originLocation === "user-collection") {
tab.value.document.saveContext = { for (const tab of activeTabs.value) {
...tab.value.document.saveContext, if (tab.document.saveContext?.originLocation === "user-collection") {
folderPath: tab.value.document.saveContext.folderPath.replace( const { folderPath } = tab.document.saveContext
oldFolderPath,
newFolderPath if (folderPath.startsWith(draggedCollectionIndex)) {
), const newFolderPath = folderPath.replace(
draggedCollectionIndex,
destinationCollectionIndex
)
tab.document.saveContext = {
...tab.document.saveContext,
folderPath: newFolderPath,
}
}
return
}
if (
tab.document.saveContext?.originLocation === "workspace-user-collection"
) {
const requestHandleRef = tab.document.saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return false
}
const { requestID } = requestHandleRef.value.data
const collectionID = requestID.split("/").slice(0, -1).join("/")
const requestIndex = requestID.split("/").slice(-1)[0]
if (collectionID.startsWith(draggedCollectionIndex)) {
const newCollectionID = collectionID.replace(
draggedCollectionIndex,
destinationCollectionIndex
)
const newRequestID = `${newCollectionID}/${requestIndex}`
tab.document.saveContext = {
...tab.document.saveContext,
requestID: newRequestID,
}
requestHandleRef.value.data = {
...requestHandleRef.value.data,
collectionID: newCollectionID,
requestID: newRequestID,
}
} }
} }
} }
@@ -166,6 +243,33 @@ function removeDuplicatesAndKeepLast(arr: HoppInheritedProperty["headers"]) {
return result return result
} }
function getSaveContextCollectionID(
saveContext: HoppRESTSaveContext | HoppGQLSaveContext | undefined
): string | undefined {
if (!saveContext) {
return
}
const { originLocation } = saveContext
if (originLocation === "team-collection") {
return saveContext.collectionID
}
if (originLocation === "user-collection") {
return saveContext.folderPath
}
const requestHandleRef = saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return
}
// TODO: Remove `collectionID` and obtain it from `requestID`
return requestHandleRef.value.data.collectionID
}
export function updateInheritedPropertiesForAffectedRequests( export function updateInheritedPropertiesForAffectedRequests(
path: string, path: string,
inheritedProperties: HoppInheritedProperty, inheritedProperties: HoppInheritedProperty,
@@ -177,22 +281,17 @@ export function updateInheritedPropertiesForAffectedRequests(
const effectedTabs = tabService.getTabsRefTo((tab) => { const effectedTabs = tabService.getTabsRefTo((tab) => {
const saveContext = tab.document.saveContext const saveContext = tab.document.saveContext
const saveContextPath = const collectionID = getSaveContextCollectionID(saveContext)
saveContext?.originLocation === "team-collection" return collectionID?.startsWith(path) ?? false
? saveContext.collectionID
: saveContext?.folderPath
return saveContextPath?.startsWith(path) ?? false
}) })
effectedTabs.map((tab) => { effectedTabs.map((tab) => {
const inheritedParentID = const inheritedParentID =
tab.value.document.inheritedProperties?.auth.parentID tab.value.document.inheritedProperties?.auth.parentID
const contextPath = const contextPath = getSaveContextCollectionID(
tab.value.document.saveContext?.originLocation === "team-collection" tab.value.document.saveContext
? tab.value.document.saveContext.collectionID )
: tab.value.document.saveContext?.folderPath
const effectedPath = folderPathCloseToSaveContext( const effectedPath = folderPathCloseToSaveContext(
inheritedParentID, inheritedParentID,
@@ -227,21 +326,6 @@ export function updateInheritedPropertiesForAffectedRequests(
}) })
} }
function resetSaveContextForAffectedRequests(folderPath: string) {
const tabService = getService(RESTTabService)
const tabs = tabService.getTabsRefTo((tab) => {
return (
tab.document.saveContext?.originLocation === "user-collection" &&
tab.document.saveContext.folderPath.startsWith(folderPath)
)
})
for (const tab of tabs) {
tab.value.document.saveContext = null
tab.value.document.isDirty = true
}
}
/** /**
* Reset save context to null if requests are deleted from the team collection or its folder * Reset save context to null if requests are deleted from the team collection or its folder
* only runs when collection or folder is deleted * only runs when collection or folder is deleted

View File

@@ -3,9 +3,10 @@ import {
HoppGQLRequest, HoppGQLRequest,
HoppRESTRequest, HoppRESTRequest,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import { getAffectedIndexes } from "./affectedIndex"
import { RESTTabService } from "~/services/tab/rest"
import { getService } from "~/modules/dioc" import { getService } from "~/modules/dioc"
import { RESTTabService } from "~/services/tab/rest"
import { getAffectedIndexes } from "./affectedIndex"
/** /**
* Resolve save context on reorder * Resolve save context on reorder
@@ -29,30 +30,76 @@ export function resolveSaveContextOnRequestReorder(payload: {
if (newIndex > lastIndex) newIndex-- // there is a issue when going down? better way to resolve this? if (newIndex > lastIndex) newIndex-- // there is a issue when going down? better way to resolve this?
if (lastIndex === newIndex) return if (lastIndex === newIndex) return
const affectedIndexes = getAffectedIndexes( const affectedIndices = getAffectedIndexes(
lastIndex, lastIndex,
newIndex === -1 ? length! : newIndex newIndex === -1 ? length! : newIndex
) )
// if (newIndex === -1) remove it from the map because it will be deleted // if (newIndex === -1) remove it from the map because it will be deleted
if (newIndex === -1) affectedIndexes.delete(lastIndex) if (newIndex === -1) affectedIndices.delete(lastIndex)
const tabService = getService(RESTTabService) const tabService = getService(RESTTabService)
const tabs = tabService.getTabsRefTo((tab) => { const tabs = tabService.getTabsRefTo((tab) => {
return ( if (tab.document.saveContext?.originLocation === "user-collection") {
tab.document.saveContext?.originLocation === "user-collection" && return (
tab.document.saveContext.folderPath === folderPath && tab.document.saveContext.folderPath === folderPath &&
affectedIndexes.has(tab.document.saveContext.requestIndex) affectedIndices.has(tab.document.saveContext.requestIndex)
) )
}
if (
tab.document.saveContext?.originLocation !== "workspace-user-collection"
) {
return false
}
const requestHandleRef = tab.document.saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return false
}
const { requestID } = requestHandleRef.value.data
const collectionID = requestID.split("/").slice(0, -1).join("/")
const requestIndex = parseInt(requestID.split("/").slice(-1)[0])
return collectionID === folderPath && affectedIndices.has(requestIndex)
}) })
for (const tab of tabs) { for (const tab of tabs) {
if (tab.value.document.saveContext?.originLocation === "user-collection") { if (tab.value.document.saveContext?.originLocation === "user-collection") {
const newIndex = affectedIndexes.get( const newIndex = affectedIndices.get(
tab.value.document.saveContext?.requestIndex tab.value.document.saveContext?.requestIndex
)! )!
tab.value.document.saveContext.requestIndex = newIndex tab.value.document.saveContext.requestIndex = newIndex
} }
if (
tab.value.document.saveContext?.originLocation !==
"workspace-user-collection"
) {
return
}
const requestHandleRef = tab.value.document.saveContext.requestHandle?.get()
if (!requestHandleRef || requestHandleRef.value.type === "invalid") {
return
}
const { requestID } = requestHandleRef.value.data
const requestIDArr = requestID.split("/")
const requestIndex = affectedIndices.get(
parseInt(requestIDArr[requestIDArr.length - 1])
)!
requestIDArr[requestIDArr.length - 1] = requestIndex.toString()
requestHandleRef.value.data.requestID = requestIDArr.join("/")
requestHandleRef.value.data.collectionID = requestIDArr
.slice(0, -1)
.join("/")
} }
} }
@@ -67,7 +114,7 @@ export function getRequestsByPath(
if (pathArray.length === 1) { if (pathArray.length === 1) {
const latestVersionedRequests = currentCollection.requests.filter( const latestVersionedRequests = currentCollection.requests.filter(
(req): req is HoppRESTRequest => req.v === "3" (req): req is HoppRESTRequest => req.v === "4"
) )
return latestVersionedRequests return latestVersionedRequests
@@ -78,7 +125,7 @@ export function getRequestsByPath(
} }
const latestVersionedRequests = currentCollection.requests.filter( const latestVersionedRequests = currentCollection.requests.filter(
(req): req is HoppRESTRequest => req.v === "3" (req): req is HoppRESTRequest => req.v === "4"
) )
return latestVersionedRequests return latestVersionedRequests

View File

@@ -281,9 +281,9 @@ export const runGQLOperation = async (options: RunQueryOptions) => {
} }
} else if (auth.authType === "api-key") { } else if (auth.authType === "api-key") {
const { key, value, addTo } = auth const { key, value, addTo } = auth
if (addTo === "Headers") { if (addTo === "HEADERS") {
finalHeaders[key] = value finalHeaders[key] = value
} else if (addTo === "Query params") { } else if (addTo === "QUERY_PARAMS") {
params[key] = value params[key] = value
} }
} }

View File

@@ -1,32 +1,31 @@
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { platform } from "~/platform"
/** /**
* Create a downloadable file from a collection and prompts the user to download it. * Create a downloadable file from a collection and prompts the user to download it.
* @param collectionJSON - JSON string of the collection * @param collectionJSON - JSON string of the collection
* @param name - Name of the collection set as the file name * @param name - Name of the collection set as the file name
*/ */
export const initializeDownloadCollection = ( export const initializeDownloadFile = async (
collectionJSON: string, collectionJSON: string,
name: string | null name: string | null
) => { ) => {
const file = new Blob([collectionJSON], { type: "application/json" }) const result = await platform.io.saveFileWithDialog({
const a = document.createElement("a") data: collectionJSON,
const url = URL.createObjectURL(file) contentType: "application/json",
a.href = url suggestedFilename: `${name ?? "collection"}.json`,
filters: [
{
name: "Hoppscotch Collection/Environment JSON file",
extensions: ["json"],
},
],
})
if (name) { if (result.type === "unknown" || result.type === "saved") {
a.download = `${name}.json` return E.right("state.download_started")
} else {
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
} }
document.body.appendChild(a) return E.left("state.download_failed")
a.click()
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
}, 1000)
return E.right("state.download_started")
} }

View File

@@ -1,5 +0,0 @@
import { HoppCollection } from "@hoppscotch/data"
export const myCollectionsExporter = (myCollections: HoppCollection[]) => {
return JSON.stringify(myCollections, null, 2)
}

View File

@@ -260,7 +260,7 @@ const resolveOpenAPIV3SecurityObj = (
return { return {
authType: "api-key", authType: "api-key",
authActive: true, authActive: true,
addTo: "Headers", addTo: "HEADERS",
key: scheme.name, key: scheme.name,
value: "", value: "",
} }
@@ -268,7 +268,7 @@ const resolveOpenAPIV3SecurityObj = (
return { return {
authType: "api-key", authType: "api-key",
authActive: true, authActive: true,
addTo: "Query params", addTo: "QUERY_PARAMS",
key: scheme.in, key: scheme.in,
value: "", value: "",
} }
@@ -430,7 +430,7 @@ const resolveOpenAPIV2SecurityScheme = (
// V2 only supports in: header and in: query // V2 only supports in: header and in: query
return { return {
authType: "api-key", authType: "api-key",
addTo: scheme.in === "header" ? "Headers" : "Query params", addTo: scheme.in === "header" ? "HEADERS" : "QUERY_PARAMS",
authActive: true, authActive: true,
key: scheme.name, key: scheme.name,
value: "", value: "",

View File

@@ -150,8 +150,8 @@ const getHoppReqAuth = (item: Item): HoppRESTAuth => {
), ),
addTo: addTo:
(getVariableValue(auth.apikey, "in") ?? "query") === "query" (getVariableValue(auth.apikey, "in") ?? "query") === "query"
? "Query params" ? "QUERY_PARAMS"
: "Headers", : "HEADERS",
} }
} else if (auth.type === "bearer") { } else if (auth.type === "bearer") {
return { return {

View File

@@ -132,30 +132,23 @@ function generateKeybindingString(ev: KeyboardEvent): ShortcutKey | null {
function getPressedKey(ev: KeyboardEvent): Key | null { function getPressedKey(ev: KeyboardEvent): Key | null {
// Sometimes the property code is not available on the KeyboardEvent object // Sometimes the property code is not available on the KeyboardEvent object
const val = (ev.code ?? "").toLowerCase() const key = (ev.key ?? "").toLowerCase()
// Check arrow keys // Check arrow keys
if (val === "arrowup") return "up" if (key.startsWith("arrow")) {
else if (val === "arrowdown") return "down" return key.slice(5) as Key
else if (val === "arrowleft") return "left" }
else if (val === "arrowright") return "right"
// Check letter keys // Check letter keys
const isLetter = val.startsWith("key") const isLetter = key.length === 1 && key >= "a" && key <= "z"
if (isLetter) return val.substring(3) as Key if (isLetter) return key as Key
// Check if number keys // Check if number keys
const isDigit = val.startsWith("digit") const isDigit = key.length === 1 && key >= "0" && key <= "9"
if (isDigit) return val.substring(5) as Key if (isDigit) return key as Key
// Check if slash // Check if slash, period or enter
if (val === "slash") return "/" if (key === "/" || key === "." || key === "enter") return key
// Check if period
if (val === "period") return "."
// Check if enter
if (val === "enter") return "enter"
// If no other cases match, this is not a valid key // If no other cases match, this is not a valid key
return null return null

View File

@@ -3,8 +3,37 @@ import { HoppRESTResponse } from "../types/HoppRESTResponse"
import { HoppTestResult } from "../types/HoppTestResult" import { HoppTestResult } from "../types/HoppTestResult"
import { RESTOptionTabs } from "~/components/http/RequestOptions.vue" import { RESTOptionTabs } from "~/components/http/RequestOptions.vue"
import { HoppInheritedProperty } from "../types/HoppInheritedProperties" import { HoppInheritedProperty } from "../types/HoppInheritedProperties"
import { Handle } from "~/services/new-workspace/handle"
import { WorkspaceRequest } from "~/services/new-workspace/workspace"
export type HoppRESTSaveContext = export type HoppRESTSaveContext =
| {
/**
* The origin source of the request
*/
// TODO: Make this `user-collection` after porting all usages
// Future TODO: Keep separate types for the IDs (specific to persistence) & `requestHandle` (only existing at runtime)
originLocation: "workspace-user-collection"
/**
* ID of the workspace
*/
workspaceID?: string
/**
* ID of the provider
*/
providerID?: string
/**
* Path to the request in the collection tree
*/
requestID?: string
/**
* Handle to the request open in the tab
*/
requestHandle?: Handle<WorkspaceRequest>
}
| { | {
/** /**
* The origin source of the request * The origin source of the request

View File

@@ -50,6 +50,7 @@ export default class TeamListAdapter {
} }
public dispose() { public dispose() {
this.teamList$.next([])
this.isDispose = true this.isDispose = true
clearTimeout(this.timeoutHandle as any) clearTimeout(this.timeoutHandle as any)
this.timeoutHandle = null this.timeoutHandle = null

View File

@@ -201,7 +201,7 @@ export class TeamSearchService extends Service {
expandingCollections: Ref<string[]> = ref([]) expandingCollections: Ref<string[]> = ref([])
expandedCollections: Ref<string[]> = ref([]) expandedCollections: Ref<string[]> = ref([])
// FUTURE-TODO: ideally this should return the search results / formatted results instead of directly manipulating the result set // TODO: ideally this should return the search results / formatted results instead of directly manipulating the result set
// eg: do the spotlight formatting in the spotlight searcher and not here // eg: do the spotlight formatting in the spotlight searcher and not here
searchTeams = async (query: string, teamID: string) => { searchTeams = async (query: string, teamID: string) => {
if (!query.length) { if (!query.length) {

View File

@@ -17,3 +17,24 @@ export type HoppInheritedProperty = {
inheritedHeader: HoppRESTHeader | GQLHeader inheritedHeader: HoppRESTHeader | GQLHeader
}[] }[]
} }
type ModifiedAuth<T, AuthType> = {
[K in keyof T]: K extends "inheritedAuth" ? Extract<T[K], AuthType> : T[K]
}
type ModifiedHeaders<T, HeaderType> = {
[K in keyof T]: K extends "inheritedHeader" ? Extract<T[K], HeaderType> : T[K]
}
type ModifiedHoppInheritedProperty<AuthType, HeaderType> = {
auth: ModifiedAuth<HoppInheritedProperty["auth"], AuthType>
headers: ModifiedHeaders<
HoppInheritedProperty["headers"][number],
HeaderType
>[]
}
export type HoppInheritedRESTProperty = ModifiedHoppInheritedProperty<
HoppRESTAuth,
HoppRESTHeader
>

View File

@@ -20,28 +20,29 @@ export type Picked =
pickedType: "my-collection" pickedType: "my-collection"
collectionIndex: number collectionIndex: number
} }
| { // TODO: Enable this when rest of the implementation is in place
pickedType: "teams-request" // | {
requestID: string // pickedType: "teams-request"
} // requestID: string
| { // }
pickedType: "teams-folder" // | {
folderID: string // pickedType: "teams-folder"
} // folderID: string
| { // }
pickedType: "teams-collection" // | {
collectionID: string // pickedType: "teams-collection"
} // collectionID: string
| { // }
pickedType: "gql-my-request" // | {
folderPath: string // pickedType: "gql-my-request"
requestIndex: number // folderPath: string
} // requestIndex: number
| { // }
pickedType: "gql-my-folder" // | {
folderPath: string // pickedType: "gql-my-folder"
} // folderPath: string
| { // }
pickedType: "gql-my-collection" // | {
collectionIndex: number // pickedType: "gql-my-collection"
} // collectionIndex: number
// }

View File

@@ -96,7 +96,7 @@ export const getComputedAuthHeaders = (
}) })
} else if (request.auth.authType === "api-key") { } else if (request.auth.authType === "api-key") {
const { key, addTo } = request.auth const { key, addTo } = request.auth
if (addTo === "Headers" && key) { if (addTo === "HEADERS" && key) {
headers.push({ headers.push({
active: true, active: true,
key: parseTemplateString(key, envVars), key: parseTemplateString(key, envVars),

View File

@@ -0,0 +1,17 @@
/**
* Create a function that will only run the given function once and caches the result.
*/
export function lazy<T>(fn: () => T): () => T {
let funcRan = false
let result: T | null = null
return () => {
if (!funcRan) {
result = fn()
funcRan = true
return result
}
return result!
}
}

View File

@@ -3,36 +3,40 @@ import { createApp } from "vue"
import { initializeApp } from "./helpers/app" import { initializeApp } from "./helpers/app"
import { initBackendGQLClient } from "./helpers/backend/GQLClient" import { initBackendGQLClient } from "./helpers/backend/GQLClient"
import { performMigrations } from "./helpers/migrations" import { performMigrations } from "./helpers/migrations"
import { getService } from "./modules/dioc"
import { PlatformDef, setPlatformDef } from "./platform" import { PlatformDef, setPlatformDef } from "./platform"
import { PersonalWorkspaceProviderService } from "./services/new-workspace/providers/personal.workspace"
import { TestWorkspaceProviderService } from "./services/new-workspace/providers/test.workspace"
import { PersistenceService } from "./services/persistence"
import "nprogress/nprogress.css"
import "../assets/scss/styles.scss"
import "../assets/scss/tailwind.scss" import "../assets/scss/tailwind.scss"
import "../assets/themes/themes.scss" import "../assets/themes/themes.scss"
import "../assets/scss/styles.scss"
import "nprogress/nprogress.css"
import "unfonts.css"
import App from "./App.vue" import App from "./App.vue"
import { getService } from "./modules/dioc"
import { PersistenceService } from "./services/persistence"
export function createHoppApp(el: string | Element, platformDef: PlatformDef) { export function createHoppApp(el: string | Element, platformDef: PlatformDef) {
setPlatformDef(platformDef) setPlatformDef(platformDef)
const app = createApp(App) const app = createApp(App)
// Some basic work that needs to be done before module inits even
initBackendGQLClient()
initializeApp()
HOPP_MODULES.forEach((mod) => mod.onVueAppInit?.(app)) HOPP_MODULES.forEach((mod) => mod.onVueAppInit?.(app))
platformDef.addedHoppModules?.forEach((mod) => mod.onVueAppInit?.(app)) platformDef.addedHoppModules?.forEach((mod) => mod.onVueAppInit?.(app))
// Some basic work that needs to be done before module inits even
initBackendGQLClient()
initializeApp()
// TODO: Explore possibilities of moving this invocation to the service constructor // TODO: Explore possibilities of moving this invocation to the service constructor
// `toast` was coming up as `null` in the previous attempts // `toast` was coming up as `null` in the previous attempts
getService(PersistenceService).setupLocalPersistence() getService(PersistenceService).setupLocalPersistence()
performMigrations() performMigrations()
// TODO: Remove this
getService(TestWorkspaceProviderService)
getService(PersonalWorkspaceProviderService)
app.mount(el) app.mount(el)
console.info( console.info(

View File

@@ -73,7 +73,7 @@ import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { InvocationTriggers, defineActionHandler } from "~/helpers/actions" import { InvocationTriggers, defineActionHandler } from "~/helpers/actions"
import { hookKeybindingsListener } from "~/helpers/keybindings" import { hookKeybindingsListener } from "~/helpers/keybindings"
import { applySetting } from "~/newstore/settings" import { applySetting, toggleSetting } from "~/newstore/settings"
import { platform } from "~/platform" import { platform } from "~/platform"
import { HoppSpotlightSessionEventData } from "~/platform/analytics" import { HoppSpotlightSessionEventData } from "~/platform/analytics"
import { PersistenceService } from "~/services/persistence" import { PersistenceService } from "~/services/persistence"
@@ -97,6 +97,8 @@ const t = useI18n()
const persistenceService = useService(PersistenceService) const persistenceService = useService(PersistenceService)
const spotlightService = useService(SpotlightService) const spotlightService = useService(SpotlightService)
const HAS_OPENED_SPOTLIGHT = useSetting("HAS_OPENED_SPOTLIGHT")
onBeforeMount(() => { onBeforeMount(() => {
if (!mdAndLarger.value) { if (!mdAndLarger.value) {
rightSidebar.value = false rightSidebar.value = false
@@ -160,6 +162,7 @@ defineActionHandler("modals.search.toggle", (_, trigger) => {
}) })
showSearch.value = !showSearch.value showSearch.value = !showSearch.value
!HAS_OPENED_SPOTLIGHT.value && toggleSetting("HAS_OPENED_SPOTLIGHT")
}) })
defineActionHandler("modals.support.toggle", () => { defineActionHandler("modals.support.toggle", () => {

View File

@@ -1,5 +1,5 @@
import { HoppModule } from "." import { HoppModule } from "."
import { Container, Service } from "dioc" import { Container, ServiceClassInstance } from "dioc"
import { diocPlugin } from "dioc/vue" import { diocPlugin } from "dioc/vue"
import { DebugService } from "~/services/debug.service" import { DebugService } from "~/services/debug.service"
import { platform } from "~/platform" import { platform } from "~/platform"
@@ -22,7 +22,7 @@ if (import.meta.env.DEV) {
* services. Please use `useService` if within components or try to convert your * services. Please use `useService` if within components or try to convert your
* legacy subsystem into a service if possible. * legacy subsystem into a service if possible.
*/ */
export function getService<T extends typeof Service<any> & { ID: string }>( export function getService<T extends ServiceClassInstance<any>>(
service: T service: T
): InstanceType<T> { ): InstanceType<T> {
return serviceContainer.bind(service) return serviceContainer.bind(service)
@@ -30,11 +30,10 @@ export function getService<T extends typeof Service<any> & { ID: string }>(
export default <HoppModule>{ export default <HoppModule>{
onVueAppInit(app) { onVueAppInit(app) {
// TODO: look into this
// @ts-expect-error Something weird with Vue versions
app.use(diocPlugin, { app.use(diocPlugin, {
container: serviceContainer, container: serviceContainer,
}) })
for (const service of platform.addedServices ?? []) { for (const service of platform.addedServices ?? []) {
serviceContainer.bind(service) serviceContainer.bind(service)
} }

View File

@@ -319,6 +319,7 @@ const restCollectionDispatchers = defineDispatchers({
) )
return {} return {}
} }
// We get the index path to the folder itself, // We get the index path to the folder itself,
// we have to find the folder containing the target folder, // we have to find the folder containing the target folder,
// so we pop the last path index // so we pop the last path index
@@ -690,17 +691,11 @@ const restCollectionDispatchers = defineDispatchers({
// if the destination is null, we are moving to the end of the list // if the destination is null, we are moving to the end of the list
if (destinationRequestIndex === null) { if (destinationRequestIndex === null) {
// move to the end of the list // TODO: Verify if this can be safely removed
targetLocation.requests.push( targetLocation.requests.push(
targetLocation.requests.splice(requestIndex, 1)[0] targetLocation.requests.splice(requestIndex, 1)[0]
) )
resolveSaveContextOnRequestReorder({
lastIndex: requestIndex,
newIndex: targetLocation.requests.length,
folderPath: destinationCollectionPath,
})
return { return {
state: newState, state: newState,
} }
@@ -708,12 +703,6 @@ const restCollectionDispatchers = defineDispatchers({
reorderItems(targetLocation.requests, requestIndex, destinationRequestIndex) reorderItems(targetLocation.requests, requestIndex, destinationRequestIndex)
resolveSaveContextOnRequestReorder({
lastIndex: requestIndex,
newIndex: destinationRequestIndex,
folderPath: destinationCollectionPath,
})
return { return {
state: newState, state: newState,
} }

View File

@@ -1,9 +1,10 @@
import { pluck, distinctUntilChanged } from "rxjs/operators"
import { cloneDeep, defaultsDeep, has } from "lodash-es" import { cloneDeep, defaultsDeep, has } from "lodash-es"
import { Observable } from "rxjs" import { Observable } from "rxjs"
import { distinctUntilChanged, pluck } from "rxjs/operators"
import DispatchingStore, { defineDispatchers } from "./DispatchingStore" import { nextTick } from "vue"
import { platform } from "~/platform"
import type { KeysMatching } from "~/types/ts-utils" import type { KeysMatching } from "~/types/ts-utils"
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
export const HoppBgColors = ["system", "light", "dark", "black"] as const export const HoppBgColors = ["system", "light", "dark", "black"] as const
@@ -65,51 +66,67 @@ export type SettingsDef = {
SIDEBAR: boolean SIDEBAR: boolean
SIDEBAR_ON_LEFT: boolean SIDEBAR_ON_LEFT: boolean
COLUMN_LAYOUT: boolean COLUMN_LAYOUT: boolean
HAS_OPENED_SPOTLIGHT: boolean
} }
export const getDefaultSettings = (): SettingsDef => ({ export const getDefaultSettings = (): SettingsDef => {
syncCollections: true, const defaultSettings: SettingsDef = {
syncHistory: true, syncCollections: true,
syncEnvironments: true, syncHistory: true,
syncEnvironments: true,
WRAP_LINES: { WRAP_LINES: {
httpRequestBody: true, httpRequestBody: true,
httpResponseBody: true, httpResponseBody: true,
httpHeaders: true, httpHeaders: true,
httpParams: true, httpParams: true,
httpUrlEncoded: true, httpUrlEncoded: true,
httpPreRequest: true, httpPreRequest: true,
httpTest: true, httpTest: true,
httpRequestVariables: true, httpRequestVariables: true,
graphqlQuery: true, graphqlQuery: true,
graphqlResponseBody: true, graphqlResponseBody: true,
graphqlHeaders: false, graphqlHeaders: false,
graphqlVariables: false, graphqlVariables: false,
graphqlSchema: true, graphqlSchema: true,
importCurl: true, importCurl: true,
codeGen: true, codeGen: true,
cookie: true, cookie: true,
}, },
CURRENT_INTERCEPTOR_ID: "browser", // TODO: Allow the platform definition to take this place CURRENT_INTERCEPTOR_ID: "",
// TODO: Interceptor related settings should move under the interceptor systems // TODO: Interceptor related settings should move under the interceptor systems
PROXY_URL: "https://proxy.hoppscotch.io/", PROXY_URL: "https://proxy.hoppscotch.io/",
URL_EXCLUDES: { URL_EXCLUDES: {
auth: true, auth: true,
httpUser: true, httpUser: true,
httpPassword: true, httpPassword: true,
bearerToken: true, bearerToken: true,
oauth2Token: true, oauth2Token: true,
}, },
THEME_COLOR: "indigo", THEME_COLOR: "indigo",
BG_COLOR: "system", BG_COLOR: "system",
TELEMETRY_ENABLED: true, TELEMETRY_ENABLED: true,
EXPAND_NAVIGATION: false, EXPAND_NAVIGATION: false,
SIDEBAR: true, SIDEBAR: true,
SIDEBAR_ON_LEFT: false, SIDEBAR_ON_LEFT: false,
COLUMN_LAYOUT: true, COLUMN_LAYOUT: true,
})
HAS_OPENED_SPOTLIGHT: false,
}
// Wait for platform to initialize before setting CURRENT_INTERCEPTOR_ID
nextTick(() => {
applySetting(
"CURRENT_INTERCEPTOR_ID",
platform?.interceptors.default || "browser"
)
})
return defaultSettings
}
type ApplySettingPayload = { type ApplySettingPayload = {
[K in keyof SettingsDef]: { [K in keyof SettingsDef]: {

View File

@@ -31,7 +31,7 @@
</template> </template>
<template #suffix> <template #suffix>
<span <span
v-if="tab.document.isDirty" v-if="getTabDirtyStatus(tab)"
class="flex w-4 items-center justify-center text-secondary group-hover:hidden" class="flex w-4 items-center justify-center text-secondary group-hover:hidden"
> >
<svg <svg
@@ -95,24 +95,24 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted } from "vue"
import { safelyExtractRESTRequest } from "@hoppscotch/data"
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
import { useRoute } from "vue-router"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { getDefaultRESTRequest } from "~/helpers/rest/default" import { safelyExtractRESTRequest } from "@hoppscotch/data"
import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { platform } from "~/platform"
import { useReadonlyStream } from "~/composables/stream"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { InspectionService } from "~/services/inspection"
import { HeaderInspectorService } from "~/services/inspection/inspectors/header.inspector"
import { EnvironmentInspectorService } from "~/services/inspection/inspectors/environment.inspector"
import { ResponseInspectorService } from "~/services/inspection/inspectors/response.inspector"
import { cloneDeep } from "lodash-es" import { cloneDeep } from "lodash-es"
import { RESTTabService } from "~/services/tab/rest" import { onMounted, ref } from "vue"
import { HoppTab } from "~/services/tab" import { useRoute } from "vue-router"
import { useReadonlyStream } from "~/composables/stream"
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
import { HoppRESTDocument } from "~/helpers/rest/document" import { HoppRESTDocument } from "~/helpers/rest/document"
import { platform } from "~/platform"
import { InspectionService } from "~/services/inspection"
import { EnvironmentInspectorService } from "~/services/inspection/inspectors/environment.inspector"
import { HeaderInspectorService } from "~/services/inspection/inspectors/header.inspector"
import { ResponseInspectorService } from "~/services/inspection/inspectors/response.inspector"
import { HoppTab } from "~/services/tab"
import { RESTTabService } from "~/services/tab/rest"
const savingRequest = ref(false) const savingRequest = ref(false)
const confirmingCloseForTabID = ref<string | null>(null) const confirmingCloseForTabID = ref<string | null>(null)
@@ -193,7 +193,7 @@ const inspectionService = useService(InspectionService)
const removeTab = (tabID: string) => { const removeTab = (tabID: string) => {
const tabState = tabs.getTabRef(tabID).value const tabState = tabs.getTabRef(tabID).value
if (tabState.document.isDirty) { if (getTabDirtyStatus(tabState)) {
confirmingCloseForTabID.value = tabID confirmingCloseForTabID.value = tabID
} else { } else {
tabs.closeTab(tabState.id) tabs.closeTab(tabState.id)
@@ -202,8 +202,10 @@ const removeTab = (tabID: string) => {
} }
const closeOtherTabsAction = (tabID: string) => { const closeOtherTabsAction = (tabID: string) => {
const isTabDirty = tabs.getTabRef(tabID).value?.document.isDirty
const dirtyTabCount = tabs.getDirtyTabsCount() const dirtyTabCount = tabs.getDirtyTabsCount()
const isTabDirty = getTabDirtyStatus(tabs.getTabRef(tabID).value)
// If current tab is dirty, so we need to subtract 1 from the dirty tab count // If current tab is dirty, so we need to subtract 1 from the dirty tab count
const balanceDirtyTabCount = isTabDirty ? dirtyTabCount - 1 : dirtyTabCount const balanceDirtyTabCount = isTabDirty ? dirtyTabCount - 1 : dirtyTabCount
@@ -268,15 +270,24 @@ const onCloseConfirmSaveTab = () => {
* Called when the user confirms they want to save the tab * Called when the user confirms they want to save the tab
*/ */
const onResolveConfirmSaveTab = () => { const onResolveConfirmSaveTab = () => {
if (tabs.currentActiveTab.value.document.saveContext) { const { saveContext } = tabs.currentActiveTab.value.document
invokeAction("request.save")
if (confirmingCloseForTabID.value) { // There're two cases where the save request under a collection modal should open
tabs.closeTab(confirmingCloseForTabID.value) // 1. Attempting to save a request that is not under a collection (When the save context is not available)
confirmingCloseForTabID.value = null // 2. Deleting a request from the collection tree and attempting to save it while closing the respective tab (When the request handle is invalid)
} if (
} else { !saveContext ||
savingRequest.value = true (saveContext.originLocation === "workspace-user-collection" &&
saveContext.requestHandle?.get().value.type === "invalid")
) {
return (savingRequest.value = true)
}
invokeAction("request.save")
if (confirmingCloseForTabID.value) {
tabs.closeTab(confirmingCloseForTabID.value)
confirmingCloseForTabID.value = null
} }
} }
@@ -304,6 +315,17 @@ const shareTabRequest = (tabID: string) => {
} }
} }
const getTabDirtyStatus = (tab: HoppTab<HoppRESTDocument>) => {
if (tab.document.isDirty) {
return true
}
return (
tab.document.saveContext?.originLocation === "workspace-user-collection" &&
tab.document.saveContext.requestHandle?.get().value.type === "invalid"
)
}
defineActionHandler("contextmenu.open", ({ position, text }) => { defineActionHandler("contextmenu.open", ({ position, text }) => {
if (text) { if (text) {
contextMenu.value = { contextMenu.value = {

View File

@@ -8,14 +8,15 @@ import { AnalyticsPlatformDef } from "./analytics"
import { InterceptorsPlatformDef } from "./interceptors" import { InterceptorsPlatformDef } from "./interceptors"
import { HoppModule } from "~/modules" import { HoppModule } from "~/modules"
import { InspectorsPlatformDef } from "./inspectors" import { InspectorsPlatformDef } from "./inspectors"
import { Service } from "dioc" import { ServiceClassInstance } from "dioc"
import { IOPlatformDef } from "./io" import { IOPlatformDef } from "./io"
import { SpotlightPlatformDef } from "./spotlight" import { SpotlightPlatformDef } from "./spotlight"
import { Ref } from "vue"
export type PlatformDef = { export type PlatformDef = {
ui?: UIPlatformDef ui?: UIPlatformDef
addedHoppModules?: HoppModule[] addedHoppModules?: HoppModule[]
addedServices?: Array<typeof Service<unknown> & { ID: string }> addedServices?: Array<ServiceClassInstance<unknown>>
auth: AuthPlatformDef auth: AuthPlatformDef
analytics?: AnalyticsPlatformDef analytics?: AnalyticsPlatformDef
io: IOPlatformDef io: IOPlatformDef
@@ -45,6 +46,11 @@ export type PlatformDef = {
* If a value is not given, then the value is assumed to be true * If a value is not given, then the value is assumed to be true
*/ */
promptAsUsingCookies?: boolean promptAsUsingCookies?: boolean
/**
* Whether to show the A/B testing workspace switcher click login flow or not
*/
workspaceSwitcherLogin?: Ref<boolean>
} }
} }

View File

@@ -1,4 +1,4 @@
import { Service } from "dioc" import { Container, ServiceClassInstance } from "dioc"
import { Inspector } from "~/services/inspection" import { Inspector } from "~/services/inspection"
/** /**
@@ -8,8 +8,9 @@ export type PlatformInspectorsDef = {
// We are keeping this as the only mode for now // We are keeping this as the only mode for now
// So that if we choose to add other modes, we can do without breaking // So that if we choose to add other modes, we can do without breaking
type: "service" type: "service"
service: typeof Service<unknown> & { ID: string } & { // TODO: I don't think this type is effective, we have to come up with a better impl
new (): Service & Inspector service: ServiceClassInstance<unknown> & {
new (c: Container): Inspector
} }
} }

View File

@@ -1,12 +1,13 @@
import { Service } from "dioc" import { Container, ServiceClassInstance } from "dioc"
import { Interceptor } from "~/services/interceptor.service" import { Interceptor } from "~/services/interceptor.service"
export type PlatformInterceptorDef = export type PlatformInterceptorDef =
| { type: "standalone"; interceptor: Interceptor } | { type: "standalone"; interceptor: Interceptor }
| { | {
type: "service" type: "service"
service: typeof Service<unknown> & { ID: string } & { // TODO: I don't think this type is effective, we have to come up with a better impl
new (): Service & Interceptor service: ServiceClassInstance<unknown> & {
new (c: Container): Interceptor
} }
} }

View File

@@ -1,10 +1,10 @@
import { Service } from "dioc" import { Container, ServiceClassInstance } from "dioc"
import { SpotlightSearcher } from "~/services/spotlight" import { SpotlightSearcher } from "~/services/spotlight"
export type SpotlightPlatformDef = { export type SpotlightPlatformDef = {
additionalSearchers?: Array< additionalSearchers?: Array<
typeof Service<unknown> & { ID: string } & { ServiceClassInstance<unknown> & {
new (): Service & SpotlightSearcher new (c: Container): SpotlightSearcher
} }
> >
} }

View File

@@ -31,9 +31,7 @@ export class ExtensionInspectorService extends Service implements Inspector {
private readonly inspection = this.bind(InspectionService) private readonly inspection = this.bind(InspectionService)
constructor() { override onServiceInit() {
super()
this.inspection.registerInspector(this) this.inspection.registerInspector(this)
} }

View File

@@ -133,9 +133,7 @@ export class ExtensionInterceptorService
public selectable = { type: "selectable" as const } public selectable = { type: "selectable" as const }
constructor() { override onServiceInit() {
super()
this.listenForExtensionStatus() this.listenForExtensionStatus()
} }

View File

@@ -13,15 +13,17 @@ export const browserIODef: IOPlatformDef = {
const url = URL.createObjectURL(file) const url = URL.createObjectURL(file)
a.href = url a.href = url
a.download = pipe( a.download =
url, opts.suggestedFilename ??
S.split("/"), pipe(
RNEA.last, url,
S.split("#"), S.split("/"),
RNEA.head, RNEA.last,
S.split("?"), S.split("#"),
RNEA.head RNEA.head,
) S.split("?"),
RNEA.head
)
document.body.appendChild(a) document.body.appendChild(a)
a.click() a.click()

View File

@@ -24,9 +24,7 @@ export class EnvironmentMenuService extends Service implements ContextMenu {
private readonly contextMenu = this.bind(ContextMenuService) private readonly contextMenu = this.bind(ContextMenuService)
constructor() { override onServiceInit() {
super()
this.contextMenu.registerMenu(this) this.contextMenu.registerMenu(this)
} }

Some files were not shown because too many files have changed in this diff Show More