Projekt

Obecné

Profil

« Předchozí | Další » 

Revize c7d2ced1

Přidáno uživatelem Václav Honzík před více než 2 roky(ů)

From feature/#9196_Connect_Frontend_With_Backend into develop

Zobrazit rozdíly:

.gitignore
1
.idea
backend/src/main/java/cz/zcu/kiv/backendapi/BackendApiApplication.java
11 11
	public static final Logger LOGGER = Logger.getLogger(BackendApiApplication.class.getName());
12 12

  
13 13
	public static void main(String[] args) {
14
		LOGGER.info("Starting ...");
15 14
		SpringApplication.run(BackendApiApplication.class, args);
15
		LOGGER.info("Swagger is running at: http://localhost:8080/swagger-ui.html");
16 16
	}
17 17

  
18 18
}
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/CatalogController.java
1
package cz.zcu.kiv.backendapi.catalog;
2

  
3
import lombok.RequiredArgsConstructor;
4
import org.springframework.http.HttpStatus;
5
import org.springframework.http.ResponseEntity;
6
import org.springframework.web.bind.annotation.*;
7

  
8
import java.util.List;
9
import java.util.UUID;
10

  
11
/**
12
 * Controller for catalog
13
 */
14
@RestController
15
@RequiredArgsConstructor
16
@RequestMapping("/catalog")
17
public class CatalogController {
18

  
19
    /**
20
     * Catalog service
21
     */
22
    private final ICatalogService catalogService;
23

  
24
    @PostMapping("")
25
    public void addCatalogEntry(@RequestBody CatalogDto catalogDto) {
26
        catalogService.saveCatalogEntry(catalogDto);
27
    }
28

  
29
    /**
30
     * Returns catalog
31
     *
32
     * @return catalog
33
     */
34
    @GetMapping("")
35
    public ResponseEntity<List<CatalogDto>> getAllUsers() {
36
        return new ResponseEntity<>(catalogService.getCatalog(), HttpStatus.OK);
37
    }
38

  
39
    /**
40
     * Updates catalog entry with given ID
41
     *
42
     * @param id         ID
43
     * @param catalogDto catalog DTO
44
     */
45
    @PutMapping("/{id}")
46
    public void updateCatalogEntry(@PathVariable UUID id, @RequestBody CatalogDto catalogDto) {
47
        catalogService.updateCatalogEntry(id, catalogDto);
48
    }
49

  
50
    /**
51
     * Deletes catalog entry with given ID
52
     *
53
     * @param id ID
54
     */
55
    @DeleteMapping("/{id}")
56
    public void deleteCatalogEntry(@PathVariable UUID id) {
57
        catalogService.deleteCatalogEntry(id);
58
    }
59
}
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/CatalogDto.java
1
package cz.zcu.kiv.backendapi.catalog;
2

  
3
import lombok.Data;
4

  
5
import java.util.Collections;
6
import java.util.Set;
7
import java.util.UUID;
8

  
9
/**
10
 * Class representing catalog DTO
11
 */
12
@Data
13
public class CatalogDto {
14
    /**
15
     * Id
16
     */
17
    private UUID id;
18

  
19
    /**
20
     * Name of geographic entry
21
     */
22
    private String name;
23

  
24
    /**
25
     * Certainty
26
     */
27
    private int certainty;
28

  
29
    /**
30
     * Longitude
31
     */
32
    private double longitude;
33

  
34
    /**
35
     * Latitude
36
     */
37
    private double latitude;
38

  
39
    /**
40
     * Bibliography
41
     */
42
    private Set<String> bibliography = Collections.emptySet();
43

  
44
    /**
45
     * Countries
46
     */
47
    private Set<String> countries = Collections.emptySet();
48

  
49
    /**
50
     * Written forms
51
     */
52
    private Set<String> writtenForms = Collections.emptySet();
53

  
54
    /**
55
     * Alternative names
56
     */
57
    private Set<String> alternativeNames = Collections.emptySet();
58

  
59
    /**
60
     * Types
61
     */
62
    private Set<String> types = Collections.emptySet();
63

  
64
}
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/CatalogEntity.java
79 79
    private Set<WrittenFormEntity> writtenForms;
80 80

  
81 81
    /**
82
     * Alternative name
82
     * Alternative names
83 83
     */
84 84
    @OneToMany(mappedBy = "catalog", cascade = CascadeType.ALL)
85 85
    @LazyCollection(LazyCollectionOption.FALSE)
......
104 104

  
105 105
        this.name = csvFields.get(1);
106 106
        List<String> stringList = processListField(csvFields.get(2));
107
        this.alternativeNames = new HashSet<>(stringList.size());
108
        for (String s : stringList) {
109
            this.alternativeNames.add(new AlternativeNameEntity(s, this));
110
        }
107
        this.alternativeNames = stringList.stream().map(s->new AlternativeNameEntity(s, this)).collect(Collectors.toSet());
108

  
111 109
        this.certainty = processIntField(csvFields.get(3));
112 110
        this.latitude = processDoubleField(csvFields.get(4));
113 111
        this.longitude = processDoubleField(csvFields.get(5));
112

  
114 113
        stringList = processListField(csvFields.get(6));
115
        this.writtenForms = new HashSet<>(stringList.size());
116
        for (String s : stringList) {
117
            this.writtenForms.add(new WrittenFormEntity(s, this));
118
        }
114
        this.writtenForms = stringList.stream().map(s->new WrittenFormEntity(s, this)).collect(Collectors.toSet());
115

  
119 116
        stringList = processListField(csvFields.get(7));
120
        this.types = new HashSet<>(stringList.size());
121
        for (String s : stringList) {
122
            this.types.add(new TypeEntity(s));
123
        }
117
        this.types = stringList.stream().map(TypeEntity::new).collect(Collectors.toSet());
118

  
124 119
        stringList = processListField(csvFields.get(8));
125
        this.countries = new HashSet<>(stringList.size());
126
        for (String s : stringList) {
127
            this.countries.add(new CountryEntity(s, this));
128
        }
120
        this.countries = stringList.stream().map(s->new CountryEntity(s, this)).collect(Collectors.toSet());
121

  
129 122
        stringList = processListField(csvFields.get(10));
130
        this.bibliography = new HashSet<>(stringList.size());
131
        for (String s : stringList) {
132
            this.bibliography.add(new BibliographyEntity(s, this));
133
        }
123
        this.bibliography = stringList.stream().map(s->new BibliographyEntity(s, this)).collect(Collectors.toSet());
134 124
    }
135 125

  
136 126
    private int processIntField(String field) {
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/CatalogRepository.java
6 6
import org.springframework.transaction.annotation.Transactional;
7 7

  
8 8
import java.util.List;
9
import java.util.Set;
9 10
import java.util.UUID;
10 11

  
11 12
/**
......
22 23
     */
23 24
    @Query("SELECT DISTINCT c FROM CatalogEntity c LEFT JOIN AlternativeNameEntity a ON c = a.catalog WHERE (?1 = c.name OR ?1 = a.name)")
24 25
    List<CatalogEntity> getCatalogEntitiesByName(String name);
26

  
27
//    @Query("SELECT DISTINCT c FROM CatalogEntity c LEFT JOIN AlternativeNameEntity a ON c = a.catalog WHERE (?1 IS NULL OR ?1 = c.name OR ?1 = a.name)" +
28
//            "LEFT JOIN TypeEntity e on c = e.catalog WHERE (?2 IS NULL OR e.)")
29
//    Set<CatalogEntity> filterCatalog(String name, String type, String country);
25 30
}
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/CatalogServiceImpl.java
1 1
package cz.zcu.kiv.backendapi.catalog;
2 2

  
3
import cz.zcu.kiv.backendapi.alternativename.AlternativeNameEntity;
4
import cz.zcu.kiv.backendapi.bibliography.BibliographyEntity;
5
import cz.zcu.kiv.backendapi.country.CountryEntity;
3 6
import cz.zcu.kiv.backendapi.type.TypeEntity;
4 7
import cz.zcu.kiv.backendapi.type.ITypeService;
8
import cz.zcu.kiv.backendapi.writtenform.WrittenFormEntity;
5 9
import lombok.RequiredArgsConstructor;
10
import lombok.extern.slf4j.Slf4j;
11
import org.springframework.security.core.userdetails.UsernameNotFoundException;
6 12
import org.springframework.stereotype.Service;
7 13
import org.springframework.transaction.annotation.Transactional;
8 14

  
9 15
import java.util.List;
16
import java.util.UUID;
17
import java.util.stream.Collectors;
10 18

  
11 19
/**
12 20
 * Catalog service implementation
......
14 22
@Service
15 23
@Transactional
16 24
@RequiredArgsConstructor
25
@Slf4j
17 26
public class CatalogServiceImpl implements ICatalogService {
27
    /**
28
     * Message for exception when catalog entry is not found by id
29
     */
30
    private static final String CATALOG_ENTRY_NOT_FOUND = "Catalog entry not found";
18 31
    /**
19 32
     * Catalog repository
20 33
     */
21 34
    private final CatalogRepository catalogRepository;
22 35

  
36
    /**
37
     * Type service
38
     */
23 39
    private final ITypeService typeService;
24 40

  
25 41
    @Override
26 42
    public void saveCatalog(List<CatalogEntity> catalogEntities) {
27
        for (CatalogEntity catalogEntity : catalogEntities) {
28
            for (TypeEntity type: catalogEntity.getTypes()                 ) {
43
        log.info("Saving catalog");
44
        catalogEntities.forEach(this::saveCatalogEntity);
45
    }
46

  
47
    @Override
48
    public void saveCatalogEntry(CatalogDto catalogDto) {
49
        log.info("Saving catalog entry");
50
        CatalogEntity catalogEntity = new CatalogEntity();
51
        convertDtoToEntity(catalogDto, catalogEntity);
52
        saveCatalogEntity(catalogEntity);
53
    }
54

  
55
    @Override
56
    public void updateCatalogEntry(UUID id, CatalogDto catalogDto) {
57
        CatalogEntity catalogEntity = catalogRepository.findById(id).orElseThrow(() -> {
58
            log.error(CATALOG_ENTRY_NOT_FOUND);
59
            throw new UsernameNotFoundException(CATALOG_ENTRY_NOT_FOUND);
60
        });
61
        convertDtoToEntity(catalogDto, catalogEntity);
62
        catalogRepository.save(catalogEntity);
63
        log.info("Catalog entry updated");
64
    }
65

  
66
    @Override
67
    public void deleteCatalogEntry(UUID id) {
68
        CatalogEntity catalogEntity = catalogRepository.findById(id).orElseThrow(() -> {
69
            log.error(CATALOG_ENTRY_NOT_FOUND);
70
            throw new UsernameNotFoundException(CATALOG_ENTRY_NOT_FOUND);
71
        });
72
        catalogRepository.delete(catalogEntity);
73
        log.info("Catalog entry deleted");
74
    }
29 75

  
30
                if (typeService.getTypeByName(type.getType()).isEmpty()){
31
                    typeService.saveType(type);
32
                }
76
    @Override
77
    public List<CatalogDto> getCatalog() {
78
        log.info("Retrieving catalog");
79
        List<CatalogEntity> entities = catalogRepository.findAll();
80
        return entities.stream().map(this::convertEntityToDto).collect(Collectors.toList());
81
    }
82

  
83
    /**
84
     * Saves catalog entity to database
85
     *
86
     * @param catalogEntity catalog entity
87
     */
88
    private void saveCatalogEntity(CatalogEntity catalogEntity) {
89
        for (TypeEntity type : catalogEntity.getTypes()) {
90
            if (typeService.getTypeByName(type.getType()).isEmpty()) {
91
                typeService.saveType(type);
33 92
            }
34 93
        }
35
        catalogRepository.saveAll(catalogEntities);
94
        catalogRepository.save(catalogEntity);
95
    }
96

  
97
    /**
98
     * Converts catalog DTO to catalog entity
99
     *
100
     * @param catalogDto    catalog DTO
101
     * @param catalogEntity catalog entity
102
     */
103
    private void convertDtoToEntity(CatalogDto catalogDto, CatalogEntity catalogEntity) {
104
        catalogEntity.setName(catalogDto.getName());
105
        catalogEntity.setCertainty(catalogDto.getCertainty());
106
        catalogEntity.setLatitude(catalogDto.getLatitude());
107
        catalogEntity.setLongitude(catalogDto.getLongitude());
108
        catalogEntity.setBibliography(catalogDto.getBibliography()
109
                .stream().map(s -> new BibliographyEntity(s, catalogEntity)).collect(Collectors.toSet()));
110
        catalogEntity.setTypes(catalogDto.getTypes()
111
                .stream().map(TypeEntity::new).collect(Collectors.toSet()));
112
        catalogEntity.setCountries(catalogDto.getCountries()
113
                .stream().map(s -> new CountryEntity(s, catalogEntity)).collect(Collectors.toSet()));
114
        catalogEntity.setAlternativeNames(catalogDto.getAlternativeNames()
115
                .stream().map(s -> new AlternativeNameEntity(s, catalogEntity)).collect(Collectors.toSet()));
116
        catalogEntity.setWrittenForms(catalogDto.getWrittenForms()
117
                .stream().map(s -> new WrittenFormEntity(s, catalogEntity)).collect(Collectors.toSet()));
36 118
    }
119

  
120
    /**
121
     * Converts catalog entity to catalog DTO
122
     *
123
     * @param entity catalog entity
124
     * @return catalog DTO
125
     */
126
    private CatalogDto convertEntityToDto(CatalogEntity entity) {
127
        CatalogDto catalogDto = new CatalogDto();
128
        catalogDto.setId(entity.getId());
129
        catalogDto.setName(entity.getName());
130
        catalogDto.setLatitude(entity.getLatitude());
131
        catalogDto.setLongitude(entity.getLongitude());
132
        catalogDto.setCertainty(entity.getCertainty());
133
        catalogDto.setBibliography(entity.getBibliography()
134
                .stream().map(BibliographyEntity::getSource).collect(Collectors.toSet()));
135
        catalogDto.setTypes(entity.getTypes()
136
                .stream().map(TypeEntity::getType).collect(Collectors.toSet()));
137
        catalogDto.setCountries(entity.getCountries()
138
                .stream().map(CountryEntity::getName).collect(Collectors.toSet()));
139
        catalogDto.setAlternativeNames(entity.getAlternativeNames()
140
                .stream().map(AlternativeNameEntity::getName).collect(Collectors.toSet()));
141
        catalogDto.setWrittenForms(entity.getWrittenForms()
142
                .stream().map(WrittenFormEntity::getForm).collect(Collectors.toSet()));
143
        return catalogDto;
144
    }
145

  
37 146
}
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/ICatalogService.java
1 1
package cz.zcu.kiv.backendapi.catalog;
2 2

  
3 3
import java.util.List;
4
import java.util.UUID;
4 5

  
5 6
/**
6 7
 * Catalog service interface
7 8
 */
8 9
public interface ICatalogService {
10
    /**
11
     * Saves list of catalog entries
12
     *
13
     * @param catalogEntities catalog entris
14
     */
9 15
    void saveCatalog(List<CatalogEntity> catalogEntities);
16

  
17
    /**
18
     * Saves one catalog entry
19
     *
20
     * @param catalogDto catalog DTO
21
     */
22
    void saveCatalogEntry(CatalogDto catalogDto);
23

  
24
    /**
25
     * Updates catalog entry with given ID
26
     *
27
     * @param id         ID
28
     * @param catalogDto catalog DTO
29
     */
30
    void updateCatalogEntry(UUID id, CatalogDto catalogDto);
31

  
32
    /**
33
     * Deletes catalog entry with given ID
34
     *
35
     * @param id ID
36
     */
37
    void deleteCatalogEntry(UUID id);
38

  
39
    /**
40
     * Returns whole catalog
41
     *
42
     * @return catalog (as list of catalog entries)
43
     */
44
    List<CatalogDto> getCatalog();
45

  
10 46
}
backend/src/main/java/cz/zcu/kiv/backendapi/exception/ApiExceptionHandler.java
43 43
    public ResponseEntity<Object> handleApiRequestException(ApiRequestException exception) {
44 44
        return new ResponseEntity<>(exception.getMessage(), exception.getHttpStatus());
45 45
    }
46

  
47
    /**
48
     * Handles illegal argument exception
49
     *
50
     * @param exception illegal argument exception
51
     * @return response entity with message and status
52
     */
53
    @ExceptionHandler(value = {IllegalArgumentException.class})
54
    public ResponseEntity<Object> handleIllegalArgumentException(IllegalArgumentException exception) {
55
        return new ResponseEntity<>(exception.getMessage(), HttpStatus.BAD_REQUEST);
56
    }
46 57
}
backend/src/main/java/cz/zcu/kiv/backendapi/security/SecurityConfig.java
74 74
                .addFilterAfter(new JwtTokenVerifier(jwtUtils, permittedUrls), JwtUsernameAndPasswordAuthenticationFilter.class);
75 75

  
76 76
    }
77
//    @Override
78
//    public void configure(WebSecurity web) throws Exception {
79
//        web.ignoring().antMatchers("/swagger-ui/**", "/v3/api-docs","/v3/api-docs/swagger-config", "/swagger-ui.html", "/swagger-ui-custom.html");
80
//    }
81

  
82 77

  
83 78
    /**
84 79
     * Sets authentication provider to authentication manager
backend/src/main/java/cz/zcu/kiv/backendapi/user/IUserService.java
15 15
     */
16 16
    UserEntity getUserByName(String username);
17 17

  
18
    /**
19
     * Registers new user
20
     *
21
     * @param userDto user DTO
22
     */
18 23
    void registerNewUser(UserDto userDto);
19 24

  
20
    void updateUser(UserDto userDto);
25
    void updateUser(String username, UserDto userDto);
21 26

  
22 27
    void deleteUser(String username);
23 28

  
29
    /**
30
     * Returns list of all users
31
     *
32
     * @return list of all users
33
     */
24 34
    List<UserDto> getAllUsers();
25 35
}
backend/src/main/java/cz/zcu/kiv/backendapi/user/UserController.java
66 66

  
67 67
    //TODO check if need, if needed probably change new dto without email, comment otherwise
68 68
    @PutMapping("/user/{username}")
69
    @ResponseStatus(value = HttpStatus.OK)
70 69
    public void updateUser(@PathVariable String username, @RequestBody @Valid UserDto userDto) {
71
        userService.updateUser(userDto);
70
        userService.updateUser(username, userDto);
72 71
    }
73 72

  
74 73
    //TODO check if needed, comment otherwise
75 74
    @DeleteMapping("/user/{username}")
76
    @ResponseStatus(value = HttpStatus.OK)
77 75
    public void deleteUser(@PathVariable String username) {
78 76
        userService.deleteUser(username);
79 77
    }
backend/src/main/java/cz/zcu/kiv/backendapi/user/UserServiceImpl.java
62 62
    }
63 63

  
64 64
    @Override
65
    public void updateUser(UserDto userDto) {
66
        UserEntity userEntity = userRepository.findByEmail(userDto.getEmail()).orElseThrow(() ->{
67
            log.error(String.format(USER_NOT_FOUND, userDto.getEmail()));
68
            throw new UsernameNotFoundException(String.format(USER_NOT_FOUND, userDto.getEmail()));
65
    public void updateUser(String username, UserDto userDto) {
66
        UserEntity userEntity = userRepository.findByEmail(username).orElseThrow(() ->{
67
            log.error(String.format(USER_NOT_FOUND, username));
68
            throw new UsernameNotFoundException(String.format(USER_NOT_FOUND, username));
69 69
        });
70 70
        convertDtoToEntity(userDto, userEntity);
71
        userEntity.setEmail(username);
71 72
        userRepository.save(userEntity);
72 73
    }
73 74

  
74
    //TODO maybe chceck if user is not deleting himself - or it might be ok
75
    //TODO maybe check if user is not deleting himself - or it might be ok
75 76
    @Override
76 77
    public void deleteUser(String username) {
77 78
        UserEntity userEntity = userRepository.findByEmail(username).orElseThrow(() ->{
frontend/package.json
1 1
{
2 2
  "name": "frontend",
3
  "type": "module",
3 4
  "version": "0.1.0",
4 5
  "private": true,
5 6
  "dependencies": {
7
    "@emotion/react": "^11.8.2",
8
    "@emotion/styled": "^11.8.1",
9
    "@mui/material": "^5.5.1",
6 10
    "@testing-library/jest-dom": "^5.14.1",
7 11
    "@testing-library/react": "^12.0.0",
8 12
    "@testing-library/user-event": "^13.2.1",
......
12 16
    "@types/react-dom": "^17.0.9",
13 17
    "axios": "^0.26.0",
14 18
    "dotenv": "^16.0.0",
19
    "formik": "^2.2.9",
15 20
    "react": "^17.0.2",
16 21
    "react-dom": "^17.0.2",
22
    "react-router-dom": "^6.2.2",
17 23
    "react-scripts": "5.0.0",
24
    "swagger-typescript-api": "^9.3.1",
25
    "ts-node": "^10.7.0",
18 26
    "typescript": "^4.4.2",
19 27
    "web-vitals": "^2.1.0"
20 28
  },
21 29
  "scripts": {
22 30
    "start": "react-scripts start",
23
    "build": "react-scripts build",
31
    "build": "react-scripts build && yarn run gen:swagger",
24 32
    "test": "react-scripts test",
25
    "eject": "react-scripts eject"
33
    "eject": "react-scripts eject",
34
    "gen:swagger": "swagger-typescript-api -p http://localhost:8080/v3/api-docs -o ./src/swagger --modular"
26 35
  },
27 36
  "eslintConfig": {
28 37
    "extends": [
frontend/src/.env
1
REACT_APP_API_BASE_URL=http://localhost:8080
frontend/src/App.test.tsx
1
import React from 'react';
2
import { render, screen } from '@testing-library/react';
3
import App from './App';
4

  
5
test('renders learn react link', () => {
6
  render(<App />);
7
  const linkElement = screen.getByText(/learn react/i);
8
  expect(linkElement).toBeInTheDocument();
9
});
frontend/src/App.tsx
1
import React from 'react';
2
import './App.css';
3
import StubComponent from './components/StubComponent'
1
import React from 'react'
2
import './App.css'
3
import { Routes, Route, Link } from 'react-router-dom'
4
import Home from './features/Home/Home'
5
import Catalog from './features/Catalog/Catalog'
6
import NotFound from './features/NotFound/NotFound'
4 7

  
5
function App() {
6
  return (
8
const App = () => (
7 9
    <div className="App">
8
      <StubComponent />
10
        <nav>
11
            <Link to="/">Home</Link>
12
            <Link to="/catalog">Catalog</Link>
13
        </nav>
14
        <Routes>
15
            <Route path="/" element={ <Home /> } />
16
            <Route path="/catalog" element={ <Catalog /> } />
17
            <Route path="*" element={ <NotFound /> } />
18
        </Routes>
9 19
    </div>
10
  );
11
}
20
)
12 21

  
13
export default App;
22
export default App
frontend/src/api/axios.ts
1
import axios from 'axios'
2
import Config from '../config/Config'
3

  
4
export default axios.create({
5
    baseURL: Config.baseUrl,
6

  
7
})
8

  
frontend/src/api/axiosInstance.ts
1
import axios from 'axios'
2
import { Store } from 'redux'
3
import config from '../config/conf'
4
import { AppStore } from '../features/redux/store'
5

  
6
let store: AppStore // this will get injected later
7

  
8
export const injectStore = (_store: Store) => {
9
    store = _store
10
}
11

  
12
const axiosInstance = axios.create({
13
    baseURL: config.baseUrl,
14
})
15

  
16
axiosInstance.interceptors.request.use(config => {
17
    config.headers = {
18
        ...config.headers,
19
        Authorization: store.getState().auth.token
20
    }
21
})
22

  
23
axiosInstance.interceptors.response.use(response => response, error => {
24

  
25
    if (error?.response?.status === 401) {
26
        const refreshToken = store.getState().auth.refreshToken
27
        // TODO send the refresh token correctly
28
        console.log('401 called they want their token refreshed');
29
    }
30

  
31
})
32

  
33
export default axiosInstance
frontend/src/components/StubComponent.tsx
1
// Example component that connects to the backend API and fetches dummy data
2

  
3
import { useState } from 'react'
4
import AxiosClient from '../services/AxiosClient'
5

  
6
const StubComponent = () => {
7

  
8
    const [text, setText] = useState<string>('')
9

  
10
    // Sends api request to the backend
11
    const fetchStub = async () => {
12
        try {
13
            const { data } = await AxiosClient.get('/stub')
14
            if (!!data && data.success) {
15
                setText(data.message as string)
16
            }
17
        }
18
        catch (err) {
19
            console.log(`An error has occurred: ${err}`)
20
        }
21
    }
22

  
23
    return (
24
        <>
25
            <h1>Welcome to the internet</h1>
26
            <p>Click the button to fetch the message</p>
27
            <p>The message: {text}</p>
28
            <button onClick={fetchStub}>Fetch the message</button>
29
        </>
30
    )
31

  
32
}
33

  
34
export default StubComponent
frontend/src/config/Config.ts
1

  
2
export default {
1
const Config = {
3 2
    baseUrl: process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080'
4
}
3
}
4

  
5
export default Config
frontend/src/config/conf.ts
1
export default {
2
    baseUrl: process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080'
3
}
frontend/src/features/Auth/AuthService.ts
1
import axios from "../../api/axios"
2
import { UserDto } from "../../swagger/data-contracts"
3
import ApiCallError from "../../utils/ApiCallError"
4

  
5
export const getAccessToken = () => localStorage.getItem('accessToken') as string | null
6
export const getRefreshToken = () => localStorage.getItem('refreshToken') as string | null
7

  
8
export const setAccessToken = (accessToken: string) => localStorage.setItem('accessToken', accessToken)
9
export const setRefreshToken = (refreshToken: string) => localStorage.setItem('refreshToken', refreshToken)
10

  
11

  
12
export const sendRegisterRequest = (userDto: UserDto) => {
13
    
14
}
15

  
16
export const sendLoginRequest = async (username: string, password: string, setLoggedInState: (loggedIn: boolean) => void) => {
17
    try {
18
        const { data } = await axios.post('/login', {username, password})
19

  
20
        if (!data) {
21
            throw new ApiCallError("An authentication error has occurred. Please try again later")
22
        }
23

  
24
        // TODO - set state as logged in
25
        const { accessToken, refreshToken } = data
26
        setAccessToken(accessToken)
27
        setRefreshToken(refreshToken)
28
        setLoggedInState(true)
29
    }
30
    catch (err) {
31

  
32
    }
33
}
frontend/src/features/Auth/Login.tsx
1

  
2
const Login = () => {
3

  
4
    return (
5
        <>
6

  
7
        </>
8
    )
9
}
10

  
11
export default Login
frontend/src/features/Auth/authReducer.ts
1
import { AnyAction } from 'redux'
2
import { persist, load } from '../../utils/statePersistence'
3

  
4
export interface UserInfo {
5
    accessToken: string | undefined // to send api requests
6
    refreshToken: string | undefined // to refresh the api key
7
    username: string // to display the username
8
    roles: string[]
9
}
10

  
11
export interface AuthState extends UserInfo {
12
    isAuthenticated: boolean // if this is false all other values should be ignored
13
}
14

  
15
const statePersistName = 'auth'
16

  
17
// Initial state when the user first starts the application
18
const initialState: AuthState = (load(statePersistName) as AuthState) || {
19
    isAuthenticated: false,
20
    username: '',
21
    roles: [],
22
}
23

  
24
// All possible actions
25
export enum AuthStateActions {
26
    LOG_IN = 'LOG_IN',
27
    LOG_OUT = 'LOG_OUT',
28
    UPDATE_ACCESS_TOKEN = 'REFRESH_ACCESS_TOKEN',
29
    UPDATE_REFRESH_TOKEN = 'UPDATE_REFRESH_TOKEN',
30
    UPDATE_TOKENS = 'UPDATE_TOKENS',
31
}
32

  
33
// Actions
34
const authReducer = (state: AuthState = initialState, action: AnyAction) => {
35
    switch (action.type) {
36
        case AuthStateActions.LOG_IN:
37
            return persist(statePersistName, {
38
                ...action.payload,
39
                isAuthenticated: true,
40
            })
41

  
42
        case AuthStateActions.LOG_OUT:
43
            return persist(statePersistName, initialState)
44

  
45
        case AuthStateActions.UPDATE_ACCESS_TOKEN:
46
            return persist(statePersistName, {
47
                ...state,
48
                accessToken: action.payload,
49
            })
50

  
51
        case AuthStateActions.UPDATE_REFRESH_TOKEN:
52
            return persist(statePersistName, {
53
                ...state,
54
                refreshToken: action.payload,
55
            })
56

  
57
        case AuthStateActions.UPDATE_TOKENS:
58
            return persist(statePersistName, { ...state, ...action.payload })
59

  
60
        default:
61
            return state
62
    }
63
}
64

  
65
export default authReducer
frontend/src/features/Catalog/Catalog.tsx
1

  
2
const Catalog = () => {
3

  
4
    return (
5
        <>
6
            <h1>Catalog</h1>
7
        </>
8
    )
9
}
10

  
11
export default Catalog
frontend/src/features/Catalog/CatalogService.ts
1
import axios from "../../api/axios"
2
import { CatalogDto } from "../../swagger/data-contracts"
3

  
4
export const getCatalogItems = async (componentMounted: boolean, setCatalog: (catalogItems: CatalogDto[]) => void, ) => {
5
    try {
6
        const { data } = await axios.get('/catalog')
7
    }
8
    catch (err: any) {
9

  
10
    }
11
}
12

  
13
export const getCatalogItem = (id: string) => {
14

  
15
}
frontend/src/features/Home/Home.tsx
1
const Home = () => {
2

  
3
    return (<>
4
    <h1>Home</h1>
5
    </>)
6
}
7

  
8
export default Home
frontend/src/features/NotFound/NotFound.tsx
1
const NotFound = () => {
2

  
3
    return (
4
        <>
5
            <h1>Page Not Found 😡😡😡</h1>
6
        </>
7
    )
8
}
9

  
10
export default NotFound
frontend/src/features/redux/store.ts
1
// Store that holds the state of the application
2

  
3
import { combineReducers, createStore } from 'redux'
4
import authReducer from '../Auth/authReducer'
5
import themeReducer from '../Theme/themeReducer'
6

  
7
const store = createStore(
8
    combineReducers({ auth: authReducer, theme: themeReducer }),
9
    {}
10
)
11

  
12
export default store
13

  
14
export type AppStore = typeof store
15

  
16
export type RootState = ReturnType<typeof store.getState>
17

  
18
export type AppDispatch = typeof store.dispatch
frontend/src/index.tsx
1
import React from 'react';
2
import ReactDOM from 'react-dom';
3
import './index.css';
4
import App from './App';
5
import reportWebVitals from './reportWebVitals';
1
import React from 'react'
2
import ReactDOM from 'react-dom'
3
import './index.css'
4
import App from './App'
5
import reportWebVitals from './reportWebVitals'
6
import { BrowserRouter } from 'react-router-dom'
6 7

  
7 8
ReactDOM.render(
8
  <React.StrictMode>
9
    <App />
10
  </React.StrictMode>,
11
  document.getElementById('root')
12
);
9
    <React.StrictMode>
10
        <BrowserRouter>
11
            <App />
12
        </BrowserRouter>
13
    </React.StrictMode>,
14
    document.getElementById('root')
15
)
13 16

  
14 17
// If you want to start measuring performance in your app, pass a function
15 18
// to log results (for example: reportWebVitals(console.log))
16 19
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17
reportWebVitals();
20
reportWebVitals()
frontend/src/logo.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
frontend/src/react-app-env.d.ts
1
/// <reference types="react-scripts" />
frontend/src/services/AxiosClient.ts
1
import axios from 'axios'
2
import Config from '../config/Config'
3

  
4
export default axios.create({
5
    baseURL: Config.baseUrl,
6
})
frontend/src/setupTests.ts
1
// jest-dom adds custom jest matchers for asserting on DOM nodes.
2
// allows you to do things like:
3
// expect(element).toHaveTextContent(/react/i)
4
// learn more: https://github.com/testing-library/jest-dom
5
import '@testing-library/jest-dom';
frontend/src/swagger/Catalog.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
import { CatalogDto } from "./data-contracts";
13
import { ContentType, HttpClient, RequestParams } from "./http-client";
14

  
15
export class Catalog<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
16
  /**
17
   * No description
18
   *
19
   * @tags catalog-controller
20
   * @name UpdateCatalogEntry
21
   * @request PUT:/catalog/{id}
22
   */
23
  updateCatalogEntry = (id: string, data: CatalogDto, params: RequestParams = {}) =>
24
    this.request<void, any>({
25
      path: `/catalog/${id}`,
26
      method: "PUT",
27
      body: data,
28
      type: ContentType.Json,
29
      ...params,
30
    });
31
  /**
32
   * No description
33
   *
34
   * @tags catalog-controller
35
   * @name DeleteCatalogEntry
36
   * @request DELETE:/catalog/{id}
37
   */
38
  deleteCatalogEntry = (id: string, params: RequestParams = {}) =>
39
    this.request<void, any>({
40
      path: `/catalog/${id}`,
41
      method: "DELETE",
42
      ...params,
43
    });
44
  /**
45
   * No description
46
   *
47
   * @tags catalog-controller
48
   * @name GetAllUsers1
49
   * @request GET:/catalog
50
   */
51
  getAllUsers1 = (params: RequestParams = {}) =>
52
    this.request<CatalogDto[], any>({
53
      path: `/catalog`,
54
      method: "GET",
55
      ...params,
56
    });
57
  /**
58
   * No description
59
   *
60
   * @tags catalog-controller
61
   * @name AddCatalogEntry
62
   * @request POST:/catalog
63
   */
64
  addCatalogEntry = (data: CatalogDto, params: RequestParams = {}) =>
65
    this.request<void, any>({
66
      path: `/catalog`,
67
      method: "POST",
68
      body: data,
69
      type: ContentType.Json,
70
      ...params,
71
    });
72
}
frontend/src/swagger/Register.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
import { UserDto } from "./data-contracts";
13
import { ContentType, HttpClient, RequestParams } from "./http-client";
14

  
15
export class Register<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
16
  /**
17
   * No description
18
   *
19
   * @tags user-controller
20
   * @name RegisterNewUser
21
   * @request POST:/register
22
   */
23
  registerNewUser = (data: UserDto, params: RequestParams = {}) =>
24
    this.request<void, any>({
25
      path: `/register`,
26
      method: "POST",
27
      body: data,
28
      type: ContentType.Json,
29
      ...params,
30
    });
31
}
frontend/src/swagger/Token.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
import { HttpClient, RequestParams } from "./http-client";
13

  
14
export class Token<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
15
  /**
16
   * No description
17
   *
18
   * @tags user-controller
19
   * @name RefreshToken
20
   * @request GET:/token/refresh
21
   */
22
  refreshToken = (params: RequestParams = {}) =>
23
    this.request<void, any>({
24
      path: `/token/refresh`,
25
      method: "GET",
26
      ...params,
27
    });
28
}
frontend/src/swagger/User.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
import { UserDto } from "./data-contracts";
13
import { ContentType, HttpClient, RequestParams } from "./http-client";
14

  
15
export class User<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
16
  /**
17
   * No description
18
   *
19
   * @tags user-controller
20
   * @name UpdateUser
21
   * @request PUT:/user/{username}
22
   */
23
  updateUser = (username: string, data: UserDto, params: RequestParams = {}) =>
24
    this.request<void, any>({
25
      path: `/user/${username}`,
26
      method: "PUT",
27
      body: data,
28
      type: ContentType.Json,
29
      ...params,
30
    });
31
  /**
32
   * No description
33
   *
34
   * @tags user-controller
35
   * @name DeleteUser
36
   * @request DELETE:/user/{username}
37
   */
38
  deleteUser = (username: string, params: RequestParams = {}) =>
39
    this.request<void, any>({
40
      path: `/user/${username}`,
41
      method: "DELETE",
42
      ...params,
43
    });
44
}
frontend/src/swagger/Users.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
import { UserDto } from "./data-contracts";
13
import { HttpClient, RequestParams } from "./http-client";
14

  
15
export class Users<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
16
  /**
17
   * No description
18
   *
19
   * @tags user-controller
20
   * @name GetAllUsers
21
   * @request GET:/users
22
   */
23
  getAllUsers = (params: RequestParams = {}) =>
24
    this.request<UserDto[], any>({
25
      path: `/users`,
26
      method: "GET",
27
      ...params,
28
    });
29
}
frontend/src/swagger/data-contracts.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
export interface UserDto {
13
  name?: string;
14
  email: string;
15
  canRead?: boolean;
16
  canWrite?: boolean;
17
  canDelete?: boolean;
18
}
19

  
20
export interface CatalogDto {
21
  /** @format uuid */
22
  id?: string;
23
  name?: string;
24

  
25
  /** @format int32 */
26
  certainty?: number;
27

  
28
  /** @format double */
29
  longitude?: number;
30

  
31
  /** @format double */
32
  latitude?: number;
33
  bibliography?: string[];
34
  countries?: string[];
35
  writtenForms?: string[];
36
  alternativeNames?: string[];
37
  types?: string[];
38
}
frontend/src/swagger/http-client.ts
1
/* eslint-disable */
2
/* tslint:disable */
3
/*
4
 * ---------------------------------------------------------------
5
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
6
 * ##                                                           ##
7
 * ## AUTHOR: acacode                                           ##
8
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9
 * ---------------------------------------------------------------
10
 */
11

  
12
export type QueryParamsType = Record<string | number, any>;
13
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
14

  
15
export interface FullRequestParams extends Omit<RequestInit, "body"> {
16
  /** set parameter to `true` for call `securityWorker` for this request */
17
  secure?: boolean;
18
  /** request path */
19
  path: string;
20
  /** content type of request body */
21
  type?: ContentType;
22
  /** query params */
23
  query?: QueryParamsType;
24
  /** format of response (i.e. response.json() -> format: "json") */
25
  format?: ResponseFormat;
26
  /** request body */
27
  body?: unknown;
28
  /** base url */
29
  baseUrl?: string;
30
  /** request cancellation token */
31
  cancelToken?: CancelToken;
32
}
33

  
34
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
35

  
36
export interface ApiConfig<SecurityDataType = unknown> {
37
  baseUrl?: string;
38
  baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
39
  securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
40
  customFetch?: typeof fetch;
41
}
42

  
43
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
44
  data: D;
45
  error: E;
46
}
47

  
48
type CancelToken = Symbol | string | number;
49

  
50
export enum ContentType {
51
  Json = "application/json",
52
  FormData = "multipart/form-data",
53
  UrlEncoded = "application/x-www-form-urlencoded",
54
}
55

  
56
export class HttpClient<SecurityDataType = unknown> {
57
  public baseUrl: string = "http://localhost:8080";
58
  private securityData: SecurityDataType | null = null;
59
  private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
60
  private abortControllers = new Map<CancelToken, AbortController>();
61
  private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
62

  
63
  private baseApiParams: RequestParams = {
64
    credentials: "same-origin",
65
    headers: {},
66
    redirect: "follow",
67
    referrerPolicy: "no-referrer",
68
  };
69

  
70
  constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
71
    Object.assign(this, apiConfig);
72
  }
73

  
74
  public setSecurityData = (data: SecurityDataType | null) => {
75
    this.securityData = data;
76
  };
77

  
78
  private encodeQueryParam(key: string, value: any) {
79
    const encodedKey = encodeURIComponent(key);
80
    return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
81
  }
82

  
83
  private addQueryParam(query: QueryParamsType, key: string) {
84
    return this.encodeQueryParam(key, query[key]);
85
  }
86

  
87
  private addArrayQueryParam(query: QueryParamsType, key: string) {
88
    const value = query[key];
89
    return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
90
  }
91

  
92
  protected toQueryString(rawQuery?: QueryParamsType): string {
93
    const query = rawQuery || {};
94
    const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
95
    return keys
96
      .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
97
      .join("&");
98
  }
99

  
100
  protected addQueryParams(rawQuery?: QueryParamsType): string {
101
    const queryString = this.toQueryString(rawQuery);
102
    return queryString ? `?${queryString}` : "";
103
  }
104

  
105
  private contentFormatters: Record<ContentType, (input: any) => any> = {
106
    [ContentType.Json]: (input: any) =>
107
      input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
108
    [ContentType.FormData]: (input: any) =>
109
      Object.keys(input || {}).reduce((formData, key) => {
110
        const property = input[key];
111
        formData.append(
112
          key,
113
          property instanceof Blob
114
            ? property
115
            : typeof property === "object" && property !== null
116
            ? JSON.stringify(property)
117
            : `${property}`,
118
        );
119
        return formData;
120
      }, new FormData()),
121
    [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
122
  };
123

  
124
  private mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
125
    return {
126
      ...this.baseApiParams,
127
      ...params1,
128
      ...(params2 || {}),
129
      headers: {
130
        ...(this.baseApiParams.headers || {}),
131
        ...(params1.headers || {}),
132
        ...((params2 && params2.headers) || {}),
... Rozdílový soubor je zkrácen, protože jeho délka přesahuje max. limit.

Také k dispozici: Unified diff