feat: initial commit
This commit is contained in:
commit
e8291f4bef
33 changed files with 10300 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
.idea/
|
||||||
|
apps/http/chalice/.chalice/deployed
|
||||||
|
apps/http/chalice/.chalice/deployments
|
||||||
|
apps/http/sls/node_modules/
|
||||||
|
apps/http/sls/.serverless/
|
||||||
|
apps/http/sls/.requirements.zip
|
||||||
|
__pycache__/
|
||||||
|
.pdm-python
|
||||||
|
.env
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
674
LICENSE
Normal file
674
LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type 'show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands 'show w' and 'show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
14
Makefile
Normal file
14
Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
install:
|
||||||
|
@echo "Installing pdm packages"
|
||||||
|
@. pdm install
|
||||||
|
@echo "Installing node packages"
|
||||||
|
@. cd apps/http/sls npm i
|
||||||
|
|
||||||
|
|
||||||
|
run--sls:
|
||||||
|
@echo "Activating virtual environment and starting serverless offline"
|
||||||
|
@. .venv/bin/activate && cd apps/http/sls && npx sls offline
|
||||||
|
|
||||||
|
deploy--sls:
|
||||||
|
@echo "Deploying via serverless framework"
|
||||||
|
@. cd apps/http/sls && pdm export -G sls -f requirements --prod > requirements.txt && npx sls deploy
|
||||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# secretsanta
|
||||||
0
apps/__init__.py
Normal file
0
apps/__init__.py
Normal file
0
apps/http/__init__.py
Normal file
0
apps/http/__init__.py
Normal file
112
apps/http/apischema.yml
Normal file
112
apps/http/apischema.yml
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
contact:
|
||||||
|
email: catalin@roboces.dev
|
||||||
|
name: 185504a9
|
||||||
|
license:
|
||||||
|
name: GNU General Public License v3.0 or later
|
||||||
|
identifier: GPL-3.0-or-later
|
||||||
|
title: Secret santa
|
||||||
|
description: Secret santa
|
||||||
|
version: 0.1.0
|
||||||
|
servers:
|
||||||
|
- url: https://secretsanta.roboces.dev/api/v1
|
||||||
|
paths:
|
||||||
|
/groups/{group_uuid}:
|
||||||
|
put:
|
||||||
|
summary: Create a new group
|
||||||
|
description: >
|
||||||
|
Create a new group for a number of people. This operation returns a list of links
|
||||||
|
that can be shared with each member in order to reveal their pair
|
||||||
|
operationId: CreateGroup
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/GroupUuid'
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Participants'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RevealLinks'
|
||||||
|
get:
|
||||||
|
summary: Get group
|
||||||
|
description: >
|
||||||
|
Given an already existing group, retrieve its links
|
||||||
|
operationId: GetGroup
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/GroupUuid'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RevealLinks'
|
||||||
|
/groups/{group_uuid}/pair/{participant}:
|
||||||
|
get:
|
||||||
|
summary: Get pair
|
||||||
|
description: >
|
||||||
|
Reveal the pair of a given participant
|
||||||
|
operationId: GetParticipantPair
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/GroupUuid'
|
||||||
|
- $ref: '#/components/parameters/Participant'
|
||||||
|
- $ref: '#/components/parameters/ResponseType'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '#/components/responses/ParticipantPairResponse'
|
||||||
|
components:
|
||||||
|
parameters:
|
||||||
|
GroupUuid:
|
||||||
|
in: path
|
||||||
|
name: group_uuid
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid4
|
||||||
|
Participant:
|
||||||
|
in: path
|
||||||
|
name: participant
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
ResponseType:
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
default: plain
|
||||||
|
enum:
|
||||||
|
- plain
|
||||||
|
- json
|
||||||
|
responses:
|
||||||
|
ParticipantPairResponse:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Participant'
|
||||||
|
text/plain:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
schemas:
|
||||||
|
RevealLinks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
format: url
|
||||||
|
Participants:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
participants:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
Participant:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
pair:
|
||||||
|
type: string
|
||||||
8
apps/http/chalice/.chalice/config.json
Normal file
8
apps/http/chalice/.chalice/config.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"version": "2.0",
|
||||||
|
"app_name": "secretsanta",
|
||||||
|
"stages": {
|
||||||
|
"dev": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
0
apps/http/chalice/__init__.py
Normal file
0
apps/http/chalice/__init__.py
Normal file
52
apps/http/chalice/app.py
Normal file
52
apps/http/chalice/app.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import anyio
|
||||||
|
from chalice import Chalice
|
||||||
|
|
||||||
|
from secretsanta.domain import Group
|
||||||
|
from secretsanta.repo import S3Repo
|
||||||
|
from secretsanta.service import (
|
||||||
|
GetGroupParticipantsService,
|
||||||
|
CreateGroupService,
|
||||||
|
GetPairService,
|
||||||
|
)
|
||||||
|
from secretsanta.settings import get_config
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
app = Chalice(app_name="secretsanta", debug=config.environment == "local")
|
||||||
|
|
||||||
|
repo = S3Repo()
|
||||||
|
|
||||||
|
|
||||||
|
def _participants2url(participants: list[str], group_uuid: str) -> list[str]:
|
||||||
|
return [
|
||||||
|
f"{config.server_url}/api/v1/groups/{group_uuid}/pair/{participant_name}"
|
||||||
|
for participant_name in participants
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/v1/groups/{group_uuid}")
|
||||||
|
def get_participants(group_uuid: str):
|
||||||
|
service = GetGroupParticipantsService(repo)
|
||||||
|
return anyio.run(service.run, group_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/v1/groups/{group_uuid}", methods=["PUT"])
|
||||||
|
def create_group(group_uuid: str):
|
||||||
|
request = app.current_request
|
||||||
|
body = request.json_body
|
||||||
|
service = CreateGroupService(repo)
|
||||||
|
participants = anyio.run(service.run, Group(uuid=group_uuid, **body)).participants
|
||||||
|
return _participants2url(participants, group_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/v1/groups/{group_uuid}/pair/{participant}")
|
||||||
|
def get_pair(group_uuid: str, participant: str):
|
||||||
|
service = GetPairService(repo)
|
||||||
|
pair = anyio.run(service.run, group_uuid, participant)
|
||||||
|
return {"pair": pair}
|
||||||
|
|
||||||
|
|
||||||
|
if config.environment == "local":
|
||||||
|
|
||||||
|
@app.route("/introspect")
|
||||||
|
def introspect():
|
||||||
|
return app.current_request.to_dict()
|
||||||
0
apps/http/litestar/__init__.py
Normal file
0
apps/http/litestar/__init__.py
Normal file
75
apps/http/litestar/app.py
Normal file
75
apps/http/litestar/app.py
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Literal, Annotated
|
||||||
|
|
||||||
|
from litestar import Controller, get, Litestar, put, Request, Response, MediaType
|
||||||
|
from litestar.params import Parameter
|
||||||
|
from pydantic import UUID4
|
||||||
|
|
||||||
|
from secretsanta.domain import Group
|
||||||
|
from secretsanta.repo import S3Repo
|
||||||
|
from secretsanta.service import (
|
||||||
|
GetGroupParticipantsService,
|
||||||
|
CreateGroupService,
|
||||||
|
GetPairService,
|
||||||
|
)
|
||||||
|
from secretsanta.settings import get_config
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CreateGroup:
|
||||||
|
participants: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PairResponse:
|
||||||
|
pair: str
|
||||||
|
|
||||||
|
|
||||||
|
class GroupController(Controller):
|
||||||
|
path = "api/v1/groups"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.repo = S3Repo()
|
||||||
|
self.get_participants_service = GetGroupParticipantsService(self.repo)
|
||||||
|
self.create_group_service = CreateGroupService(self.repo)
|
||||||
|
self.get_pair_service = GetPairService(self.repo)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _group2urls(participants: list[str], url: str) -> list[str]:
|
||||||
|
return [f"{url}/pair/{participant_name}" for participant_name in participants]
|
||||||
|
|
||||||
|
@get("/{group_uuid:uuid}")
|
||||||
|
async def get_participants(self, request: Request, group_uuid: UUID4) -> list[str]:
|
||||||
|
participants = await self.get_participants_service.run(group_uuid)
|
||||||
|
return self._group2urls(participants, request.url)
|
||||||
|
|
||||||
|
@put("/{group_uuid:uuid}")
|
||||||
|
async def create_group(
|
||||||
|
self, request: Request, group_uuid: UUID4, data: CreateGroup
|
||||||
|
) -> list[str]:
|
||||||
|
group = await self.create_group_service.run(
|
||||||
|
Group(uuid=group_uuid, **asdict(data))
|
||||||
|
)
|
||||||
|
return self._group2urls(group.participants, request.url)
|
||||||
|
|
||||||
|
@get("/{group_uuid:uuid}/pair/{participant_name:str}")
|
||||||
|
async def get_pair(
|
||||||
|
self,
|
||||||
|
group_uuid: UUID4,
|
||||||
|
participant_name: str,
|
||||||
|
response_type: Annotated[
|
||||||
|
Literal["json", "plain"], Parameter(query="type")
|
||||||
|
] = "plain",
|
||||||
|
) -> Response[str] | PairResponse:
|
||||||
|
pair = await self.get_pair_service.run(
|
||||||
|
group_uuid=group_uuid, participant=participant_name
|
||||||
|
)
|
||||||
|
if response_type == "json":
|
||||||
|
return PairResponse(pair=pair)
|
||||||
|
|
||||||
|
return Response(content=pair, media_type=MediaType.TEXT)
|
||||||
|
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
app = Litestar(route_handlers=[GroupController], debug=config.environment == "local")
|
||||||
0
apps/http/sls/__init__.py
Normal file
0
apps/http/sls/__init__.py
Normal file
43
apps/http/sls/app.py
Normal file
43
apps/http/sls/app.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import anyio
|
||||||
|
|
||||||
|
from secretsanta.domain import Group
|
||||||
|
from secretsanta.repo import S3Repo
|
||||||
|
from secretsanta.service import (
|
||||||
|
GetGroupParticipantsService,
|
||||||
|
GetPairService,
|
||||||
|
CreateGroupService,
|
||||||
|
)
|
||||||
|
from secretsanta.settings import get_config
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
repo = S3Repo()
|
||||||
|
|
||||||
|
|
||||||
|
def get_participants(event, _):
|
||||||
|
group_uuid = event["pathParameters"]["group_uuid"]
|
||||||
|
service = GetGroupParticipantsService(repo)
|
||||||
|
participants = anyio.run(service.run, group_uuid)
|
||||||
|
|
||||||
|
return {"statusCode": 200, "body": json.dumps(participants)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_pair(event, _):
|
||||||
|
group_uuid = event["pathParameters"]["group_uuid"]
|
||||||
|
participant = event["pathParameters"]["participant"]
|
||||||
|
query_params = event["queryStringParameters"] or {}
|
||||||
|
response_type = query_params.get("type", "plain")
|
||||||
|
service = GetPairService(repo)
|
||||||
|
pair = anyio.run(service.run, group_uuid, participant)
|
||||||
|
if response_type == "json":
|
||||||
|
return {"statusCode": 200, "body": json.dumps({"pair": pair})}
|
||||||
|
return {"statusCode": 200, "body": pair, "headers": {"Content-Type": "text/plain"}}
|
||||||
|
|
||||||
|
|
||||||
|
def create_group(event, _):
|
||||||
|
group_uuid = event["pathParameters"]["group_uuid"]
|
||||||
|
body = json.loads(event["body"])
|
||||||
|
service = CreateGroupService(repo)
|
||||||
|
participants = anyio.run(service.run, Group(uuid=group_uuid, **body))
|
||||||
|
return {"statusCode": 200, "body": json.dumps(participants)}
|
||||||
7235
apps/http/sls/package-lock.json
generated
Normal file
7235
apps/http/sls/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
12
apps/http/sls/package.json
Normal file
12
apps/http/sls/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"serverless": "^3.38.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"serverless-api-gateway-caching": "^1.10.3",
|
||||||
|
"serverless-api-gateway-throttling": "^2.0.3",
|
||||||
|
"serverless-offline": "^13.3.0",
|
||||||
|
"serverless-prune-plugin": "^2.0.2",
|
||||||
|
"serverless-python-requirements": "^6.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
244
apps/http/sls/requirements.txt
Normal file
244
apps/http/sls/requirements.txt
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
# This file is @generated by PDM.
|
||||||
|
# Please do not edit it manually.
|
||||||
|
|
||||||
|
aioboto3==12.0.0 \
|
||||||
|
--hash=sha256:23895e734f83e34827a38d3a08ccc3f4178cc8d4e3a7b7031a0cbf8efc875555 \
|
||||||
|
--hash=sha256:c2bbb990b4efd2e474a1e8a42a80291faf6434b2dc8678f163208d338b2dba39
|
||||||
|
aiobotocore==2.7.0 \
|
||||||
|
--hash=sha256:506591374cc0aee1bdf0ebe290560424a24af176dfe2ea7057fe1df97c4f0467 \
|
||||||
|
--hash=sha256:aec605df77ce4635a0479b50fd849aa6b640900f7b295021ecca192e1140e551
|
||||||
|
aiohttp==3.9.1 \
|
||||||
|
--hash=sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af \
|
||||||
|
--hash=sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213 \
|
||||||
|
--hash=sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01 \
|
||||||
|
--hash=sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5 \
|
||||||
|
--hash=sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183 \
|
||||||
|
--hash=sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544 \
|
||||||
|
--hash=sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821 \
|
||||||
|
--hash=sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f \
|
||||||
|
--hash=sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe \
|
||||||
|
--hash=sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f \
|
||||||
|
--hash=sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2 \
|
||||||
|
--hash=sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57 \
|
||||||
|
--hash=sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87 \
|
||||||
|
--hash=sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed \
|
||||||
|
--hash=sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0 \
|
||||||
|
--hash=sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d \
|
||||||
|
--hash=sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff \
|
||||||
|
--hash=sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587 \
|
||||||
|
--hash=sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb \
|
||||||
|
--hash=sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3 \
|
||||||
|
--hash=sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66 \
|
||||||
|
--hash=sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35 \
|
||||||
|
--hash=sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449 \
|
||||||
|
--hash=sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd \
|
||||||
|
--hash=sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b \
|
||||||
|
--hash=sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a \
|
||||||
|
--hash=sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d \
|
||||||
|
--hash=sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e \
|
||||||
|
--hash=sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d \
|
||||||
|
--hash=sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c \
|
||||||
|
--hash=sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065
|
||||||
|
aioitertools==0.11.0 \
|
||||||
|
--hash=sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394 \
|
||||||
|
--hash=sha256:42c68b8dd3a69c2bf7f2233bf7df4bb58b557bca5252ac02ed5187bbc67d6831
|
||||||
|
aiosignal==1.3.1 \
|
||||||
|
--hash=sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc \
|
||||||
|
--hash=sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17
|
||||||
|
annotated-types==0.6.0 \
|
||||||
|
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||||
|
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||||
|
anyio==4.1.0 \
|
||||||
|
--hash=sha256:56a415fbc462291813a94528a779597226619c8e78af7de0507333f700011e5f \
|
||||||
|
--hash=sha256:5a0bec7085176715be77df87fc66d6c9d70626bd752fcc85f57cdbee5b3760da
|
||||||
|
attrs==23.1.0 \
|
||||||
|
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
|
||||||
|
--hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
|
||||||
|
boto3==1.28.64 \
|
||||||
|
--hash=sha256:a5cf93b202568e9d378afdc84be55a6dedf11d30156289fe829e23e6d7dccabb \
|
||||||
|
--hash=sha256:a99150a30c038c73e89662836820a8cce914afab5ea377942a37c484b85f4438
|
||||||
|
botocore==1.31.64 \
|
||||||
|
--hash=sha256:7b709310343a5b430ec9025b2e17c0bac6b16c05f1ac1d9521dece3f10c71bac \
|
||||||
|
--hash=sha256:d8eb4b724ac437343359b318d73de0cfae0fecb24095827e56135b0ad6b44caf
|
||||||
|
frozenlist==1.4.0 \
|
||||||
|
--hash=sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01 \
|
||||||
|
--hash=sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251 \
|
||||||
|
--hash=sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b \
|
||||||
|
--hash=sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c \
|
||||||
|
--hash=sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467 \
|
||||||
|
--hash=sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1 \
|
||||||
|
--hash=sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb \
|
||||||
|
--hash=sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431 \
|
||||||
|
--hash=sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3 \
|
||||||
|
--hash=sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472 \
|
||||||
|
--hash=sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc \
|
||||||
|
--hash=sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839 \
|
||||||
|
--hash=sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f \
|
||||||
|
--hash=sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b \
|
||||||
|
--hash=sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95 \
|
||||||
|
--hash=sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f
|
||||||
|
idna==3.6 \
|
||||||
|
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||||
|
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||||
|
jmespath==1.0.1 \
|
||||||
|
--hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \
|
||||||
|
--hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe
|
||||||
|
multidict==6.0.4 \
|
||||||
|
--hash=sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9 \
|
||||||
|
--hash=sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03 \
|
||||||
|
--hash=sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710 \
|
||||||
|
--hash=sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636 \
|
||||||
|
--hash=sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49 \
|
||||||
|
--hash=sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4 \
|
||||||
|
--hash=sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed \
|
||||||
|
--hash=sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3 \
|
||||||
|
--hash=sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe \
|
||||||
|
--hash=sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c \
|
||||||
|
--hash=sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c \
|
||||||
|
--hash=sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0 \
|
||||||
|
--hash=sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a \
|
||||||
|
--hash=sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982 \
|
||||||
|
--hash=sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461 \
|
||||||
|
--hash=sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba
|
||||||
|
pydantic==2.5.2 \
|
||||||
|
--hash=sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0 \
|
||||||
|
--hash=sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd
|
||||||
|
pydantic-core==2.14.5 \
|
||||||
|
--hash=sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b \
|
||||||
|
--hash=sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8 \
|
||||||
|
--hash=sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189 \
|
||||||
|
--hash=sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d \
|
||||||
|
--hash=sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f \
|
||||||
|
--hash=sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520 \
|
||||||
|
--hash=sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4 \
|
||||||
|
--hash=sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b \
|
||||||
|
--hash=sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a \
|
||||||
|
--hash=sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68 \
|
||||||
|
--hash=sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3 \
|
||||||
|
--hash=sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634 \
|
||||||
|
--hash=sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7 \
|
||||||
|
--hash=sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459 \
|
||||||
|
--hash=sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3 \
|
||||||
|
--hash=sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf \
|
||||||
|
--hash=sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d \
|
||||||
|
--hash=sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36 \
|
||||||
|
--hash=sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937 \
|
||||||
|
--hash=sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc \
|
||||||
|
--hash=sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca \
|
||||||
|
--hash=sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260 \
|
||||||
|
--hash=sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71 \
|
||||||
|
--hash=sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb \
|
||||||
|
--hash=sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69 \
|
||||||
|
--hash=sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5 \
|
||||||
|
--hash=sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0 \
|
||||||
|
--hash=sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8 \
|
||||||
|
--hash=sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944 \
|
||||||
|
--hash=sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26 \
|
||||||
|
--hash=sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda \
|
||||||
|
--hash=sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00 \
|
||||||
|
--hash=sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe \
|
||||||
|
--hash=sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6 \
|
||||||
|
--hash=sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada \
|
||||||
|
--hash=sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4 \
|
||||||
|
--hash=sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b \
|
||||||
|
--hash=sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88 \
|
||||||
|
--hash=sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04 \
|
||||||
|
--hash=sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e \
|
||||||
|
--hash=sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144 \
|
||||||
|
--hash=sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d \
|
||||||
|
--hash=sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789 \
|
||||||
|
--hash=sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef \
|
||||||
|
--hash=sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3 \
|
||||||
|
--hash=sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc \
|
||||||
|
--hash=sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651 \
|
||||||
|
--hash=sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8 \
|
||||||
|
--hash=sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e \
|
||||||
|
--hash=sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7 \
|
||||||
|
--hash=sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27 \
|
||||||
|
--hash=sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093 \
|
||||||
|
--hash=sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077 \
|
||||||
|
--hash=sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed \
|
||||||
|
--hash=sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88 \
|
||||||
|
--hash=sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe \
|
||||||
|
--hash=sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18
|
||||||
|
pydantic-settings==2.1.0 \
|
||||||
|
--hash=sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c \
|
||||||
|
--hash=sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a
|
||||||
|
python-dateutil==2.8.2 \
|
||||||
|
--hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
|
||||||
|
--hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
|
||||||
|
python-dotenv==1.0.0 \
|
||||||
|
--hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \
|
||||||
|
--hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a
|
||||||
|
s3transfer==0.7.0 \
|
||||||
|
--hash=sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a \
|
||||||
|
--hash=sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e
|
||||||
|
six==1.16.0 \
|
||||||
|
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||||
|
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
||||||
|
sniffio==1.3.0 \
|
||||||
|
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||||
|
--hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384
|
||||||
|
typing-extensions==4.8.0 \
|
||||||
|
--hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \
|
||||||
|
--hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef
|
||||||
|
urllib3==2.0.7 \
|
||||||
|
--hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \
|
||||||
|
--hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e
|
||||||
|
wrapt==1.16.0 \
|
||||||
|
--hash=sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81 \
|
||||||
|
--hash=sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09 \
|
||||||
|
--hash=sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c \
|
||||||
|
--hash=sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060 \
|
||||||
|
--hash=sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9 \
|
||||||
|
--hash=sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b \
|
||||||
|
--hash=sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d \
|
||||||
|
--hash=sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362 \
|
||||||
|
--hash=sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc \
|
||||||
|
--hash=sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1 \
|
||||||
|
--hash=sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956 \
|
||||||
|
--hash=sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1 \
|
||||||
|
--hash=sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d \
|
||||||
|
--hash=sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36 \
|
||||||
|
--hash=sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73 \
|
||||||
|
--hash=sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b \
|
||||||
|
--hash=sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389 \
|
||||||
|
--hash=sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89 \
|
||||||
|
--hash=sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3 \
|
||||||
|
--hash=sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8 \
|
||||||
|
--hash=sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d \
|
||||||
|
--hash=sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809
|
||||||
|
yarl==1.9.4 \
|
||||||
|
--hash=sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51 \
|
||||||
|
--hash=sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce \
|
||||||
|
--hash=sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0 \
|
||||||
|
--hash=sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81 \
|
||||||
|
--hash=sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc \
|
||||||
|
--hash=sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099 \
|
||||||
|
--hash=sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129 \
|
||||||
|
--hash=sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10 \
|
||||||
|
--hash=sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142 \
|
||||||
|
--hash=sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98 \
|
||||||
|
--hash=sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7 \
|
||||||
|
--hash=sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525 \
|
||||||
|
--hash=sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c \
|
||||||
|
--hash=sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9 \
|
||||||
|
--hash=sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8 \
|
||||||
|
--hash=sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf \
|
||||||
|
--hash=sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff \
|
||||||
|
--hash=sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31 \
|
||||||
|
--hash=sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad \
|
||||||
|
--hash=sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7 \
|
||||||
|
--hash=sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2 \
|
||||||
|
--hash=sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9 \
|
||||||
|
--hash=sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984 \
|
||||||
|
--hash=sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4 \
|
||||||
|
--hash=sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074 \
|
||||||
|
--hash=sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572 \
|
||||||
|
--hash=sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1 \
|
||||||
|
--hash=sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0 \
|
||||||
|
--hash=sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe \
|
||||||
|
--hash=sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42 \
|
||||||
|
--hash=sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78 \
|
||||||
|
--hash=sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958
|
||||||
|
|
||||||
73
apps/http/sls/serverless.yml
Normal file
73
apps/http/sls/serverless.yml
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
app: secret-santa
|
||||||
|
service: secret-santa
|
||||||
|
|
||||||
|
frameworkVersion: '3'
|
||||||
|
configValidationMode: error
|
||||||
|
provider:
|
||||||
|
name: aws
|
||||||
|
runtime: python3.11
|
||||||
|
region: eu-west-1
|
||||||
|
environment:
|
||||||
|
SECRETSANTA_SERVER_URL: "https://foo.bar"
|
||||||
|
SECRETSANTA_S3_BUCKET_NAME: ${env:SECRETSANTA_S3_BUCKET_NAME}
|
||||||
|
SECRETSANTA_S3_ACCESS_KEY_ID: ${env:SECRETSANTA_S3_ACCESS_KEY_ID}
|
||||||
|
SECRETSANTA_S3_SECRET_ACCESS_KEY: ${env:SECRETSANTA_S3_SECRET_ACCESS_KEY}
|
||||||
|
|
||||||
|
package:
|
||||||
|
excludeDevDependencies: true
|
||||||
|
individually: false
|
||||||
|
patterns:
|
||||||
|
- '!./**'
|
||||||
|
- ./*.py
|
||||||
|
- ../../../secretsanta/**.py
|
||||||
|
|
||||||
|
custom:
|
||||||
|
pythonRequirements:
|
||||||
|
dockerizePip: true
|
||||||
|
zip: true
|
||||||
|
slim: true
|
||||||
|
layer: true
|
||||||
|
prune:
|
||||||
|
automatic: true
|
||||||
|
includeLayers: true
|
||||||
|
number: 1
|
||||||
|
apiGatewayCaching:
|
||||||
|
enabled: true
|
||||||
|
apiGatewayThrottling:
|
||||||
|
maxRequestsPerSecond: 100
|
||||||
|
maxConcurrentRequests: 5
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- serverless-python-requirements
|
||||||
|
- serverless-offline
|
||||||
|
- serverless-prune-plugin
|
||||||
|
- serverless-api-gateway-caching
|
||||||
|
- serverless-api-gateway-throttling
|
||||||
|
|
||||||
|
functions:
|
||||||
|
get_participants:
|
||||||
|
handler: app.get_participants
|
||||||
|
layers:
|
||||||
|
- Ref: PythonRequirementsLambdaLayer
|
||||||
|
events:
|
||||||
|
- httpApi:
|
||||||
|
path: /api/v1/groups/{group_uuid}
|
||||||
|
method: get
|
||||||
|
|
||||||
|
create_group:
|
||||||
|
handler: app.create_group
|
||||||
|
layers:
|
||||||
|
- Ref: PythonRequirementsLambdaLayer
|
||||||
|
events:
|
||||||
|
- httpApi:
|
||||||
|
path: /api/v1/groups/{group_uuid}
|
||||||
|
method: put
|
||||||
|
|
||||||
|
get_pair:
|
||||||
|
handler: app.get_pair
|
||||||
|
layers:
|
||||||
|
- Ref: PythonRequirementsLambdaLayer
|
||||||
|
events:
|
||||||
|
- httpApi:
|
||||||
|
path: /api/v1/groups/{group_uuid}/pair/{participant}
|
||||||
|
method: get
|
||||||
43
pyproject.toml
Normal file
43
pyproject.toml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
[project]
|
||||||
|
name = "secretsanta"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Secret santa generator"
|
||||||
|
authors = [
|
||||||
|
{name = "185504a9", email = "catalin@roboces.dev"},
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"pydantic>=2.5.2",
|
||||||
|
"pydantic-settings>=2.1.0",
|
||||||
|
"aioboto3>=12.0.0",
|
||||||
|
]
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
readme = "README.md"
|
||||||
|
license = {text = "GPL-3.0-or-later"}
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
litestar = [
|
||||||
|
"litestar[standard]>=2.4.2",
|
||||||
|
]
|
||||||
|
testing = [
|
||||||
|
"polyfactory>=2.12.0",
|
||||||
|
"pytest>=7.4.3",
|
||||||
|
"pytest-cov>=4.1.0",
|
||||||
|
]
|
||||||
|
linting = [
|
||||||
|
"ruff>=0.1.7",
|
||||||
|
]
|
||||||
|
chalice = [
|
||||||
|
"chalice>=1.29.0",
|
||||||
|
"anyio>=4.1.0",
|
||||||
|
]
|
||||||
|
sls = [
|
||||||
|
"anyio>=4.1.0",
|
||||||
|
]
|
||||||
|
[build-system]
|
||||||
|
requires = ["pdm-backend"]
|
||||||
|
build-backend = "pdm.backend"
|
||||||
|
|
||||||
|
[tool.pdm.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
"types-aioboto3-lite[s3]>=12.0.0",
|
||||||
|
]
|
||||||
3
sample.env
Normal file
3
sample.env
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
secretsanta_s3_bucket_name=
|
||||||
|
secretsanta_s3_secret_access_key=
|
||||||
|
secretsanta_s3_access_key_id=
|
||||||
0
secretsanta/__init__.py
Normal file
0
secretsanta/__init__.py
Normal file
44
secretsanta/domain.py
Normal file
44
secretsanta/domain.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
import random
|
||||||
|
|
||||||
|
from pydantic import BaseModel, UUID4
|
||||||
|
|
||||||
|
from secretsanta.settings import Config, get_config
|
||||||
|
|
||||||
|
|
||||||
|
class Group(BaseModel):
|
||||||
|
uuid: UUID4
|
||||||
|
participants: list[str]
|
||||||
|
pairs: dict[str, str] | None = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pair(names: list[str]) -> dict[str, str]:
|
||||||
|
names_copy = names[:]
|
||||||
|
random.shuffle(names_copy)
|
||||||
|
|
||||||
|
pairings = {}
|
||||||
|
for i, name in enumerate(names):
|
||||||
|
pair = names_copy[i]
|
||||||
|
while pair == name:
|
||||||
|
random.shuffle(names_copy)
|
||||||
|
pair = names_copy[i]
|
||||||
|
|
||||||
|
pairings[name] = pair
|
||||||
|
|
||||||
|
return pairings
|
||||||
|
|
||||||
|
|
||||||
|
class IRepo(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
async def get(self, group_uuid: str) -> Group | None:
|
||||||
|
... # pragma: no cover
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def create(self, group: Group) -> Group:
|
||||||
|
... # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
|
class IService(ABC):
|
||||||
|
def __init__(self, repo: IRepo, config: Config | None = None):
|
||||||
|
self.repo = repo
|
||||||
|
self.config = config or get_config()
|
||||||
50
secretsanta/repo.py
Normal file
50
secretsanta/repo.py
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import json
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
|
import aioboto3
|
||||||
|
import botocore
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
|
|
||||||
|
from secretsanta.domain import Group, IRepo
|
||||||
|
from secretsanta.settings import get_config
|
||||||
|
|
||||||
|
|
||||||
|
class S3Repo(IRepo):
|
||||||
|
def __init__(self):
|
||||||
|
self.config = get_config()
|
||||||
|
self.session = aioboto3.Session(
|
||||||
|
aws_access_key_id=self.config.s3_access_key_id,
|
||||||
|
aws_secret_access_key=self.config.s3_secret_access_key,
|
||||||
|
aws_session_token=self.config.s3_session_token,
|
||||||
|
region_name=self.config.s3_region,
|
||||||
|
)
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def get_s3(self):
|
||||||
|
kwargs = {}
|
||||||
|
if self.config.s3_endpoint_url:
|
||||||
|
kwargs["endpoint_url"] = str(self.config.s3_endpoint_url)
|
||||||
|
async with self.session.client("s3", **kwargs) as s3:
|
||||||
|
yield s3
|
||||||
|
|
||||||
|
async def get(self, group_uuid: str) -> Group | None:
|
||||||
|
async with self.get_s3() as s3:
|
||||||
|
try:
|
||||||
|
s3_obj = await s3.get_object(
|
||||||
|
Bucket=self.config.s3_bucket_name,
|
||||||
|
Key=f"secretsanta/{group_uuid}.json",
|
||||||
|
)
|
||||||
|
except ClientError:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
group_raw = await s3_obj["Body"].read()
|
||||||
|
return Group(**json.loads(group_raw))
|
||||||
|
|
||||||
|
async def create(self, group: Group) -> Group:
|
||||||
|
async with self.get_s3() as s3:
|
||||||
|
await s3.put_object(
|
||||||
|
Bucket=self.config.s3_bucket_name,
|
||||||
|
Key=f"secretsanta/{group.uuid}.json",
|
||||||
|
Body=group.model_dump_json(),
|
||||||
|
)
|
||||||
|
return group
|
||||||
46
secretsanta/service.py
Normal file
46
secretsanta/service.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from secretsanta.domain import Group, IService
|
||||||
|
|
||||||
|
|
||||||
|
def participants2url(participants: list[str], group_uuid: str | UUID, server_url: str):
|
||||||
|
return [
|
||||||
|
f"{server_url}/groups/{group_uuid}/pair/{participant}"
|
||||||
|
for participant in participants
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CreateGroupService(IService):
|
||||||
|
async def run(self, group: Group):
|
||||||
|
if existing_group := await self.repo.get(group.uuid):
|
||||||
|
return participants2url(
|
||||||
|
participants=existing_group.participants,
|
||||||
|
group_uuid=existing_group.uuid,
|
||||||
|
server_url=self.config.server_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
group.pairs = Group.pair(group.participants)
|
||||||
|
group = await self.repo.create(group)
|
||||||
|
|
||||||
|
return participants2url(
|
||||||
|
participants=group.participants,
|
||||||
|
group_uuid=group.uuid,
|
||||||
|
server_url=self.config.server_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GetGroupParticipantsService(IService):
|
||||||
|
async def run(self, group_uuid: str) -> list[str] | None:
|
||||||
|
group = await self.repo.get(group_uuid)
|
||||||
|
if group:
|
||||||
|
return participants2url(
|
||||||
|
participants=group.participants,
|
||||||
|
group_uuid=group.uuid,
|
||||||
|
server_url=self.config.server_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GetPairService(IService):
|
||||||
|
async def run(self, group_uuid: str, participant: str) -> str:
|
||||||
|
group = await self.repo.get(group_uuid)
|
||||||
|
return group.pairs[participant]
|
||||||
24
secretsanta/settings.py
Normal file
24
secretsanta/settings.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
from functools import lru_cache
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
from pydantic import HttpUrl
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(env_prefix="secretsanta_")
|
||||||
|
environment: Literal["local", "prod"] = "local"
|
||||||
|
server_url: str = "http://127.0.0.1:8000"
|
||||||
|
|
||||||
|
# s3
|
||||||
|
s3_endpoint_url: HttpUrl | None = None # None means AWS
|
||||||
|
s3_bucket_name: str
|
||||||
|
s3_secret_access_key: str
|
||||||
|
s3_access_key_id: str
|
||||||
|
s3_session_token: str | None = None
|
||||||
|
s3_region: str = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def get_config(**kwargs):
|
||||||
|
return Config(**kwargs)
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
6
tests/conftest.py
Normal file
6
tests/conftest.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def anyio_backend():
|
||||||
|
return "asyncio"
|
||||||
3
tests/restish/group.json
Normal file
3
tests/restish/group.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"participants": ["Tibi", "Cassius", "Ursula", "Patricia", "Sam"]
|
||||||
|
}
|
||||||
0
tests/unit/__init__.py
Normal file
0
tests/unit/__init__.py
Normal file
74
tests/unit/conftest.py
Normal file
74
tests/unit/conftest.py
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import pytest
|
||||||
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
||||||
|
from polyfactory.pytest_plugin import register_fixture
|
||||||
|
|
||||||
|
from secretsanta.domain import Group, IRepo
|
||||||
|
from secretsanta.service import (
|
||||||
|
CreateGroupService,
|
||||||
|
GetGroupParticipantsService,
|
||||||
|
GetPairService,
|
||||||
|
)
|
||||||
|
from secretsanta.settings import get_config
|
||||||
|
|
||||||
|
|
||||||
|
class TestRepo(IRepo):
|
||||||
|
def __init__(self):
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
|
async def get(self, group_uuid: str) -> Group | None:
|
||||||
|
return self.data.get(group_uuid)
|
||||||
|
|
||||||
|
async def create(self, group: Group) -> Group:
|
||||||
|
self.data[group.uuid] = group
|
||||||
|
return group
|
||||||
|
|
||||||
|
|
||||||
|
@register_fixture
|
||||||
|
class GroupFactory(ModelFactory[Group]):
|
||||||
|
__model__ = Group
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def participants(cls) -> list[str]:
|
||||||
|
number_of_participants = cls.__random__.randint(2, 10)
|
||||||
|
return [cls.__faker__.name() for _ in range(number_of_participants)]
|
||||||
|
|
||||||
|
pairs = None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def group(group_factory):
|
||||||
|
return group_factory.build()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def repo():
|
||||||
|
return TestRepo()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def repo_with_data(repo, group):
|
||||||
|
group.pairs = group.pair(group.participants)
|
||||||
|
repo.data[group.uuid] = group
|
||||||
|
return repo
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def config():
|
||||||
|
return get_config(
|
||||||
|
s3_bucket_name="foo", s3_secret_access_key="foo", s3_access_key_id="foo"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def create_group_service(repo, config):
|
||||||
|
return CreateGroupService(repo=repo, config=config)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def get_participants_service(repo, config):
|
||||||
|
return GetGroupParticipantsService(repo=repo, config=config)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def get_pair_service(repo, config):
|
||||||
|
return GetPairService(repo=repo, config=config)
|
||||||
13
tests/unit/test_pair.py
Normal file
13
tests/unit/test_pair.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from secretsanta.domain import Group
|
||||||
|
from tests.unit.conftest import GroupFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_pair(group):
|
||||||
|
group.pairs = Group.pair(group.participants)
|
||||||
|
|
||||||
|
assert len(group.pairs) == len(
|
||||||
|
group.participants
|
||||||
|
), "Number of pairs does not match participants"
|
||||||
|
|
||||||
|
for k, v in group.pairs.items():
|
||||||
|
assert k != v, "Self-pairings are not allowed"
|
||||||
54
tests/unit/test_service.py
Normal file
54
tests/unit/test_service.py
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from secretsanta.service import participants2url
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.anyio
|
||||||
|
|
||||||
|
|
||||||
|
async def test_create_group_service(create_group_service, group, config):
|
||||||
|
participants = await create_group_service.run(group)
|
||||||
|
assert (
|
||||||
|
participants2url(
|
||||||
|
participants=group.participants,
|
||||||
|
group_uuid=group.uuid,
|
||||||
|
server_url=config.server_url,
|
||||||
|
)
|
||||||
|
== participants
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("repo_with_data")
|
||||||
|
async def test_create_group_service_returns_existing_group(create_group_service, group, config, group_factory):
|
||||||
|
new_group = group_factory.build()
|
||||||
|
new_group.uuid = group.uuid
|
||||||
|
assert new_group != group
|
||||||
|
participants = await create_group_service.run(new_group)
|
||||||
|
assert (
|
||||||
|
participants2url(
|
||||||
|
participants=group.participants,
|
||||||
|
group_uuid=group.uuid,
|
||||||
|
server_url=config.server_url,
|
||||||
|
)
|
||||||
|
== participants
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("repo_with_data")
|
||||||
|
async def test_get_participants(get_participants_service, group, config):
|
||||||
|
participants = await get_participants_service.run(group.uuid)
|
||||||
|
assert (
|
||||||
|
participants2url(
|
||||||
|
participants=group.participants,
|
||||||
|
group_uuid=group.uuid,
|
||||||
|
server_url=config.server_url,
|
||||||
|
)
|
||||||
|
== participants
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("repo_with_data")
|
||||||
|
async def test_get_pair(get_pair_service, group):
|
||||||
|
assert group.pairs
|
||||||
|
for gifter, giftee in group.pairs.items():
|
||||||
|
pair = await get_pair_service.run(group.uuid, gifter)
|
||||||
|
assert pair == giftee
|
||||||
Loading…
Add table
Add a link
Reference in a new issue